1
+ package connection_util ;
2
+
3
+ import java .lang .reflect .Proxy ;
4
+ import java .sql .Connection ;
5
+ import java .sql .DriverManager ;
6
+ import java .sql .SQLException ;
7
+ import java .util .ArrayList ;
8
+ import java .util .List ;
9
+ import java .util .concurrent .ArrayBlockingQueue ;
10
+ import java .util .concurrent .BlockingQueue ;
11
+
12
+ public final class ConnectionPoolManager {
13
+ /* Извлекаем данные из application.properties */
14
+ private static final String PASSWORD_KEY = "db.password" ;
15
+ private static final String USERNAME_KEY = "db.username" ;
16
+ private static final String URL_KEY = "db.url" ;
17
+ private static final String POOL_SIZE_KEY = "db.pool.size" ;
18
+ /* Задаем размер пула по-умолчанию */
19
+ private static final Integer DEFAULT_POOL_SIZE = 10 ;
20
+ /*
21
+ Потокобезопасная блокирующая очередь для
22
+ хранения наших прокси - соединений
23
+ */
24
+ private static BlockingQueue <Connection > pool ;
25
+ /* Список наших реальных соединений */
26
+ private static List <Connection > sourceConnections ;
27
+
28
+ static {
29
+ loadDriver ();
30
+ initConnectionPool ();
31
+ }
32
+ /* Приватный конструктор данного класса */
33
+ private ConnectionPoolManager () {
34
+ }
35
+
36
+ private static void initConnectionPool () {
37
+ /* Извлекаем значение размера будущего пула соединений из файла*/
38
+ String poolSize = PropertiesUtil .get (POOL_SIZE_KEY );
39
+ /*
40
+ Существует вероятность, что в ходе извлечения данных из файла
41
+ свойств, либо в момент преобразования данных в int может произойти
42
+ ошибка (вот какая интересно?). Для того чтобы этого избежать
43
+ воспользуемся тернарным оператором и как вариант подставим дефолтный
44
+ размер пула.
45
+ */
46
+ int size = poolSize == null ? DEFAULT_POOL_SIZE : Integer .parseInt (poolSize );
47
+ /* Создаем очередь для хранения наших прокси - соединений */
48
+ pool = new ArrayBlockingQueue <>(size );
49
+ /*
50
+ Создаем список для хранения наших реальных соединений.
51
+ К нему мы обратимся в момент закрытия всех реальных
52
+ соединений.
53
+ */
54
+ sourceConnections = new ArrayList <>(size );
55
+ /*
56
+ В цикле создаем реальное и прокси - соединение с базой.
57
+ Помещаем и то и другое в свои коллекции данных.
58
+ */
59
+ for (int i = 0 ; i < size ; i ++) {
60
+ Connection connection = openConnection ();
61
+ /*
62
+ В общем случае прокси-объект - это объект, который служит
63
+ посредником для доступа к другому объекту, каким-то образом
64
+ меняя свойства или поведение этого объекта.
65
+
66
+ Так что со стороны клиента (т.е. объекта-пользователя)
67
+ поведение выглядит несколько не так, как было бы при
68
+ непосредственном доступе.
69
+
70
+ Используется также в случаях, когда прямой доступ к
71
+ используемому объекту по какой-то причине невозможен.
72
+ ('прокси - объект' - паттерн проектирования)
73
+
74
+ Самое простое — использовать java.lang.reflect.Proxy,
75
+ который является частью JDK. Этот класс может создать
76
+ прокси-класс или напрямую его инстанс.
77
+
78
+ Пользоваться прокси, встроенным в Java, очень просто.
79
+ Все что нужно — реализовать java.lang.InvocationHandler,
80
+ чтобы прокси-объект мог его вызывать.
81
+
82
+ Интерфейс InvocationHandler крайне прост и содержит только
83
+ один метод: invoke(). При его вызове, аргументы содержат
84
+ проксируемый оригинальный объект, вызванный метод
85
+ (как отражение объекта Method) и массив объектов исходных
86
+ аргументов.
87
+ */
88
+ Connection proxyConnection = (Connection ) Proxy .newProxyInstance (
89
+ ConnectionPoolManager .class .getClassLoader (),
90
+ new Class []{Connection .class },
91
+ (proxy , method , args ) -> method .getName ().equals ("close" )
92
+ ? pool .add ((Connection ) proxy )
93
+ : method .invoke (connection , args ));
94
+ pool .add (proxyConnection );
95
+ sourceConnections .add (connection );
96
+ }
97
+ }
98
+ /* Методы для получения прокси - соединения из пула */
99
+ public static Connection getConnectionFromPool () {
100
+ try {
101
+ return pool .take ();
102
+ } catch (InterruptedException e ) {
103
+ throw new RuntimeException (e );
104
+ }
105
+ }
106
+ /*
107
+ Приватный метод для создания соединения с базой данных
108
+ сделанный для защиты от случайного создания соединения
109
+ сверх размера пула.
110
+ */
111
+ private static Connection openConnection () {
112
+ try {
113
+ return DriverManager .getConnection (
114
+ PropertiesUtil .get (URL_KEY ),
115
+ PropertiesUtil .get (USERNAME_KEY ),
116
+ PropertiesUtil .get (PASSWORD_KEY )
117
+ );
118
+ } catch (SQLException e ) {
119
+ throw new RuntimeException (e );
120
+ }
121
+ }
122
+ /* Загружаем драйвер PostgreSQL */
123
+ private static void loadDriver () {
124
+ try {
125
+ Class .forName ("org.postgresql.Driver" );
126
+ } catch (ClassNotFoundException e ) {
127
+ throw new RuntimeException (e );
128
+ }
129
+ }
130
+ /* Метод закрывает реальные соединения с базой данных всего пула */
131
+ public static void closePool () {
132
+ try {
133
+ for (Connection sourceConnection : sourceConnections ) {
134
+ sourceConnection .close ();
135
+ }
136
+ } catch (SQLException e ) {
137
+ throw new RuntimeException (e );
138
+ }
139
+ }
140
+ }
0 commit comments