0%

全局组件初始化

对于组件的全局的初始化,可以使用前面所说的ImportBeanDefinitionRegistrar这个方法来指定初始化方法,以开源的队列QMQ的源码为例:

@EnableQmq实现方式

1
2
3
4
5
6
7
8
9
10
11
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(QmqConsumerRegister.class)
public @interface EnableQmq {

String appCode();

String metaServer();
}

其中QmqConsumerRegister实现代码如下

1
2
3
4
5
6
7
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
...
}



Spring在扫描包的时候,会自动的运行registerBeanDefinitions中的方法,实现组件初始化。

单实体使用

利用bean的生命周期可以优雅的实现,其中 @PostConstruct和@PreDestroy写法更容易,也比较好控制,缺点就是需要找到的对应的入口,对于源码阅读不是很友好。

阅读全文 »

这个Demo的功能是客户端向服务端发送一个Hello Netty的消息,然后服务端又把消息返回给客户端

Server端

这些都是一些公共的代码,代码比较简单,服务端负责监听端口,Handler负责处理业务逻辑

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
public class ServerProcessor {
public void process(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap bootStrap = new ServerBootstrap();
ChannelFuture cf;
bootStrap.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new ServerMessageHandler());//server的handler
}
});

try {
cf = bootStrap.bind(8099).sync();//监听8099端口
System.out.println("8099:binded...");
cf.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}

public static void main(String[] args) {
ServerProcessor serverProcessor = new ServerProcessor();
serverProcessor.process();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@ChannelHandler.Sharable
public class ServerMessageHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务已启动,等待客户连接");
}


//接收客户端发送的消息
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf)msg;/*netty实现的缓冲区*/
System.out.println("Server Accept:"+in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)/*flush掉所有的数据*/
.addListener(ChannelFutureListener.CLOSE);/*当flush完成后,关闭连接*/
}
}

Client端

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
public class ClientProcessor {

public void process() {
EventLoopGroup group =new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>(){
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new ClientMessageHandler());

}
});

ChannelFuture future = b.connect("127.0.0.1", 8099).sync();//连接端口

future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}

}

public static void main(String[] args) {
ClientProcessor clientProcessor = new ClientProcessor();
clientProcessor.process();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ClientMessageHandler extends SimpleChannelInboundHandler<ByteBuf> {

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.channel().writeAndFlush(Unpooled.copiedBuffer("Hello Netty", CharsetUtil.UTF_8));
ctx.fireChannelActive();
}


@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("NettyClient accetp:" + byteBuf.toString(CharsetUtil.UTF_8));
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}

}


阅读全文 »

配置HikariDataSource测试环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
public class RzConfig {
@Bean
public DataSource dataSource() {
//此处也可以写到配置文件中
final HikariConfig cpConfig = new HikariConfig();
cpConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
cpConfig.setJdbcUrl("jdbc:mysql://localhost:3306/rzframe?characterEncoding=utf8&useSSL=true&autoReconnect=true&failOverReadOnly=false&serverTimezone=UTC");
cpConfig.setUsername("root");
cpConfig.setPassword("abc12345");
cpConfig.setMaximumPoolSize(10);
HikariDataSource hikariDataSource = new HikariDataSource(cpConfig);
return hikariDataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
}

编写事务代码和执行

  1. @Configuration增加@EnableTransactionManagement注解

  2. 注册PlatformTransactionManager

1
2
3
4
5
@Bean
public PlatformTransactionManager platformTransactionManager(){
return new DataSourceTransactionManager(dataSource());
}

  1. 写一个Service主动抛出异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Service
public class MenuService {
@Autowired
JdbcTemplate jdbcTemplate;

@Transactional
public void insertMenu() throws Exception {
String sql = "insert into `rz_menu` (`menu_name`, `menu_src`, `menu_icon`, `menu_status`, `parentmenu_id`, `create_time`, `dataChange_lastTime`) values('test','/','fa-desktop','0','0',now(),now());";
jdbcTemplate.execute(sql);
int i = 1 / 0;

}
}

执行Service 程序运行报错,数据也无法正常的插入。

阅读全文 »

获得接口的实现类有点困难

在Java中,由于反射的局限性,无法直接获取一个接口的所有实现子类,所以为了能够实现一个接口动态的注入实现的子类对象,需要借助ServiceLoader

简单的Demo使用

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

public interface IService {
void doSomeThing();
}

public class DefalutService implements IService{
@Override
public void doSomeThing() {
RzLogger.info("默认服务");
}
}

public class LogService implements IService {
@Override
public void doSomeThing() {
RzLogger.info("日志服务");
}
}


public static void main(String[] args) {
ServiceLoader<IService> loader = ServiceLoader.load(IService.class);
for (IService service : loader) {
service.doSomeThing();
}
}

如果直接运行,可以发现没有任何结果,需要在META-INF\services创建一个文件xxx.xxx.IService(是接口类的全类名) ,内容是两个子类的全类名:

1
2
learnJava.rz08.DefalutService
learnJava.rz08.LogService

再次运行结果:

1
2
21:55:48,873  INFO [main] (RzLogger.java:12) - 默认服务
21:55:48,877 INFO [main] (RzLogger.java:12) - 日志服务
阅读全文 »

AOP的使用


所谓AOP的思想是现在程序设计中常用的一种设计思想,主要是为了能够更好的监控程序的运行的情况。 在Spring中AOP的思想展现的淋漓尽致。

实现一个简单的AOP。

1
2
3
4
5
6
7
8
@Component
public class OrderBiz {

public void doSomething(int x, int y) {
System.out.println(x + y);
}
}

增加一个AOP的类:

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
@Aspect
public class LogAspector {

//此处是一个入口,定义为OrderBiz的所有方法
@Pointcut("execution(public void learnJava.spring.sprz05.OrderBiz.*(..))")
public void pointCut(){}

@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
System.out.println(joinPoint);
RzLogger.info("方法开始执行....");
}

@After("pointCut()")
public void logEnd() {
RzLogger.info("方法执行完成....");
}

@AfterReturning("pointCut()")
public void logReturn() {
RzLogger.info("方法执行结束返回数据....");
}
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void logException(Exception ex) {
RzLogger.info("方法执行异常....");
RzLogger.error(JsonUtils.serializeObject(ex));
}

@Around("pointCut()")
public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
RzLogger.info("Around开始");
Object proceed = joinPoint.proceed();
RzLogger.info("Around结束");
return proceed;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@EnableAspectJAutoProxy //不可缺少
public class ConfigMain {

@Bean
public OrderBiz getOrderBiz(){
return new OrderBiz();
}

@Bean
public LogAspector getLogAspector(){
return new LogAspector();
}
}


主方法的调用

1
2
3
4
5
6
7
public static void main(String[] args) {

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigMain.class);
OrderBiz bean = applicationContext.getBean(OrderBiz.class);
bean.doSomething(1, 3);

}

阅读全文 »

在Spring容器中,实例对象都会有三个过程,创建,初始化和消亡。

创建 Bean 的方式


@Bean 注解指定方法

1
2
3
4
@Bean(initMethod = "init",destroyMethod = "destroy")
public Dog getDog(){
return new Dog();
}

InitializingBean和DisposableBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Cat implements InitializingBean, DisposableBean {
public Cat(){
RzLogger.info("cat create");
}
@Override
public void destroy() throws Exception {
RzLogger.info("cat destroy");
}

/**
* 在容器创建的时候调用
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
RzLogger.info("cat afterPropertiesSet");
}
}

阅读全文 »

IOC的使用

@Configuration是入口

在Spring中,在一个类的上面增加@Configuration注解,这个就表示当前为配置类,可以代替xml形式的配置形式。在类中可以定义各种bean的定义,这些定义会被AnnotationConfigApplicationContext容器扫描。

1
2
3
4
5
@Configuration
public class RzConfig
{

}

注入的@Configuration对象可以通过configApplicationContext.getBeanDefinitionNames()方法获得对应的集合

1
2
3
4
5
AnnotationConfigApplicationContext configApplicationContext=new AnnotationConfigApplicationContext(RzConfig.class);
String[] beanDefinitionNames = configApplicationContext.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++) {
RzLogger.info("获得注入的类:"+beanDefinitionNames[i]);
}
1
2
3
4
5
6
23:24:18,150  INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalAutowiredAnnotationProcessor
23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.annotation.internalCommonAnnotationProcessor
23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.event.internalEventListenerProcessor
23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:org.springframework.context.event.internalEventListenerFactory
23:24:18,154 INFO [main] (RzLogger.java:12) - 获得注入的类:rzConfig

@Bean的使用

阅读全文 »

发现addShutdownHook

在阅读QMQ的源码的时候,在Server端启动的时候,注册了一个shutdown的代码,具体的代码如下:

1
2
Runtime.getRuntime().addShutdownHook(new Thread(wrapper::destroy));

addShutdownHook作用

addShutdownHook方法可以添加一个指定的线程来在Java程序退出的时候做一些事情,在以下几个场景会被调用:

  1. 程序运行完成后退出
  2. 使用Ctrl+C时终端退出
  3. 调用系统退出的方法, System.exit(0)

具体的使用Demo如下:

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
public class AppMain {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(AppMain::exit01));
Runtime.getRuntime().addShutdownHook(new Thread(AppMain::exit02));
Runtime.getRuntime().addShutdownHook(new Thread(AppMain::exit03));
while (true){
System.out.println(".....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
public static void exit01(){
System.out.println("exit01");
}
public static void exit02(){
System.out.println("exit02");
}
public static void exit03(){
System.out.println("exit03");
}
}

执行当执行退出的时候:
程序退出

阅读全文 »

最近研究的东西有点杂,也有点乱,有点不太成体系,也不够深入,收获还是有的,但是并没有想象中的多。总感觉最近在憋着一股劲准备写点什么东西,但是心里总感觉不踏实,总有一种危机感在身边。

本着越是乱 越是要整理的原则,自己列了下学习和复习的技术路线:

基础知识

  1. Kafka的原理机制
  2. Spring cloud 原理 和服务降级Hytrix
  3. Dubbo和CDubbo的部署和原理
  4. Spring的使用和源码(源码可以晚点)
  5. 并发编程的使用和原理,着重理解锁的细节,能够手写实现锁
  6. 类加载机制和垃圾回收过程的细化分析,着重理解CMS和G1垃圾回收器的不同
  7. JVM调优的命令复习
  8. RxJava的学习
  9. Netty的复习,着重学习ByteBuf的深入使用和细节,能够实现TCP,UDP和WebSocket的通信
  10. IO的模型,AIO的Netty实现,NIO和BIO的概念
  11. Redis的深入和Lua语言,能够实现一个通用的分布式锁,支持重入。
  12. 写一个Redis的客户端和Redis哨兵,实现主从切换和选举
  13. ASM织入相关知识
  14. 数据结构的在复习

系统学习

  1. 操作系统原理的复习
  2. 编译原理,实现一个简单的编译器
  3. 虚拟机的编写,写一个简单的语言编译器

框架源码阅读

  1. QMQ
  2. RocketMQ
  3. CtripDal
  4. Mybatis
  5. Dubbo和CDubbo
  6. SpringMVC

语言

阅读全文 »