diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/pom.xml b/2022-02/spring-04-aop/aop-demo/aop-custom/pom.xml
new file mode 100644
index 00000000..32ea04a7
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-custom
+ 1.0
+
+
+ 11
+ 11
+ UTF-8
+ 1.9.8
+
+
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/Main.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/Main.java
new file mode 100644
index 00000000..fc62438c
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/Main.java
@@ -0,0 +1,47 @@
+package ru.otus.demo;
+
+import ru.otus.demo.aop.LoggingAspect;
+import ru.otus.demo.dao.BookDao;
+import ru.otus.demo.dao.BookDaoSimple;
+import ru.otus.demo.dao.PersonDao;
+import ru.otus.demo.dao.PersonDaoSimple;
+import ru.otus.demo.service.BookService;
+import ru.otus.demo.service.BookServiceImpl;
+import ru.otus.demo.service.PersonService;
+import ru.otus.demo.service.PersonServiceImpl;
+import ru.otus.demo.aop.SimpleWeaver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Main {
+
+ public static void main(String[] args) {
+
+ SimpleWeaver weaver = new SimpleWeaver();
+ LoggingAspect aspect = new LoggingAspect();
+
+ // Как будто контекст с бинами (пока без аспекта)
+ Map, Object> context = new HashMap<>();
+ context.put(PersonDao.class, new PersonDaoSimple());
+ context.put(BookDao.class, new BookDaoSimple());
+
+ // Колхозный вивинг
+ context.forEach((key, value) -> context.put(key, weaver.weave(value, aspect)));
+
+ // Как будто достаем из контекста дао (уже с примененным аспектом)
+ PersonDao personDaoProxy = (PersonDao) context.get(PersonDao.class);
+ BookDao bookDaoProxy = (BookDao) context.get(BookDao.class);
+
+ // Внедряем в сервисы
+ PersonService personService = new PersonServiceImpl(personDaoProxy);
+ BookService bookService = new BookServiceImpl(bookDaoProxy);
+
+ // Используем сервисы
+ personService.getByName("Вася");
+ System.out.println("-------------------------------------------------");
+ bookService.getByTitle("Укротители велосипедов");
+ }
+
+
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/LoggingAspect.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/LoggingAspect.java
new file mode 100644
index 00000000..e8537435
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/LoggingAspect.java
@@ -0,0 +1,20 @@
+package ru.otus.demo.aop;
+
+import org.aspectj.lang.JoinPoint;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class LoggingAspect {
+
+ public void logBefore(JoinPoint joinPoint) {
+ System.out.println("Прокси : " + joinPoint.getThis().getClass().getName());
+ System.out.println("Класс : " + joinPoint.getTarget().getClass().getName());
+
+ System.out.println("Вызов метода : " + joinPoint.getSignature().getName());
+ System.out.println("Аргументы метода : " + Arrays.stream(joinPoint.getArgs())
+ .map(Objects::toString)
+ .collect(Collectors.joining(", ")));
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleAOPInvocationHandler.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleAOPInvocationHandler.java
new file mode 100644
index 00000000..52489be2
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleAOPInvocationHandler.java
@@ -0,0 +1,24 @@
+package ru.otus.demo.aop;
+
+import org.aspectj.lang.JoinPoint;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+public class SimpleAOPInvocationHandler implements InvocationHandler {
+ private final Object target;
+ private final LoggingAspect aspect;
+
+ public SimpleAOPInvocationHandler(Object target, LoggingAspect aspect) {
+ this.target = target;
+ this.aspect = aspect;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ JoinPoint joinPoint = new SimpleJoinPoint(target,
+ proxy, args, method);
+ aspect.logBefore(joinPoint);
+ return method.invoke(target, args);
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleJoinPoint.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleJoinPoint.java
new file mode 100644
index 00000000..b80fd999
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleJoinPoint.java
@@ -0,0 +1,67 @@
+package ru.otus.demo.aop;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.reflect.SourceLocation;
+
+import java.lang.reflect.Method;
+
+public class SimpleJoinPoint implements JoinPoint {
+ private final Object targetClass;
+ private final Object proxyClass;
+ private final Object[] args;
+ private final Signature targetMethodSignature;
+
+ public SimpleJoinPoint(Object target, Object proxy, Object[] args,
+ Method targetMethod) {
+ this.targetClass = target;
+ this.proxyClass = proxy;
+ this.args = args;
+ this.targetMethodSignature = new SimpleSignature(targetMethod);
+ }
+
+ @Override
+ public String toShortString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toLongString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getThis() {
+ return proxyClass;
+ }
+
+ @Override
+ public Object getTarget() {
+ return targetClass;
+ }
+
+ @Override
+ public Object[] getArgs() {
+ return args;
+ }
+
+ @Override
+ public Signature getSignature() {
+ return targetMethodSignature;
+ }
+
+ @Override
+ public SourceLocation getSourceLocation() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getKind() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public StaticPart getStaticPart() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleSignature.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleSignature.java
new file mode 100644
index 00000000..1a525c24
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleSignature.java
@@ -0,0 +1,43 @@
+package ru.otus.demo.aop;
+
+import org.aspectj.lang.Signature;
+
+import java.lang.reflect.Method;
+
+public class SimpleSignature implements Signature {
+ private final Method targetMethod;
+
+ public SimpleSignature(Method targetMethod) {
+ this.targetMethod = targetMethod;
+ }
+
+ @Override
+ public String toShortString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toLongString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getName() {
+ return targetMethod.getName();
+ }
+
+ @Override
+ public int getModifiers() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Class> getDeclaringType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getDeclaringTypeName() {
+ throw new UnsupportedOperationException();
+ }
+}
\ No newline at end of file
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleWeaver.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleWeaver.java
new file mode 100644
index 00000000..7e46c016
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleWeaver.java
@@ -0,0 +1,16 @@
+package ru.otus.demo.aop;
+
+import ru.otus.demo.Main;
+
+import java.lang.reflect.Proxy;
+
+public class SimpleWeaver {
+ @SuppressWarnings("unchecked")
+ public T weave(T classInstance, LoggingAspect aspect) {
+ return (T) Proxy.newProxyInstance(
+ Main.class.getClassLoader(),
+ classInstance.getClass().getInterfaces(),
+ new SimpleAOPInvocationHandler(classInstance, aspect)
+ );
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDao.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDao.java
new file mode 100644
index 00000000..17a905ba
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDao.java
@@ -0,0 +1,8 @@
+package ru.otus.demo.dao;
+
+import ru.otus.demo.domain.Book;
+
+public interface BookDao {
+
+ Book findByTitle(String title);
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDaoSimple.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDaoSimple.java
new file mode 100644
index 00000000..56a0b41d
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDaoSimple.java
@@ -0,0 +1,10 @@
+package ru.otus.demo.dao;
+
+import ru.otus.demo.domain.Book;
+
+public class BookDaoSimple implements BookDao {
+ @Override
+ public Book findByTitle(String title) {
+ return new Book(title);
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDao.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDao.java
new file mode 100644
index 00000000..15563d64
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.demo.dao;
+
+import ru.otus.demo.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDaoSimple.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..7adbfddf
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDaoSimple.java
@@ -0,0 +1,11 @@
+package ru.otus.demo.dao;
+
+import ru.otus.demo.domain.Person;
+
+
+public class PersonDaoSimple implements PersonDao {
+
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Book.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Book.java
new file mode 100644
index 00000000..d54a119f
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Book.java
@@ -0,0 +1,14 @@
+package ru.otus.demo.domain;
+
+public class Book {
+
+ private String title;
+
+ public Book(String name) {
+ this.title = name;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Person.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Person.java
new file mode 100644
index 00000000..80ee7acf
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Person.java
@@ -0,0 +1,20 @@
+package ru.otus.demo.domain;
+
+public class Person {
+
+ private String name;
+ private int age;
+
+ public Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookService.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookService.java
new file mode 100644
index 00000000..713e4e82
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookService.java
@@ -0,0 +1,8 @@
+package ru.otus.demo.service;
+
+import ru.otus.demo.domain.Book;
+
+public interface BookService {
+
+ Book getByTitle(String title);
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookServiceImpl.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookServiceImpl.java
new file mode 100644
index 00000000..713dc0e5
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookServiceImpl.java
@@ -0,0 +1,17 @@
+package ru.otus.demo.service;
+
+import ru.otus.demo.dao.BookDao;
+import ru.otus.demo.domain.Book;
+
+public class BookServiceImpl implements BookService {
+ private final BookDao bookDao;
+
+ public BookServiceImpl(BookDao bookDao) {
+ this.bookDao = bookDao;
+ }
+
+ @Override
+ public Book getByTitle(String title) {
+ return bookDao.findByTitle(title);
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonService.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonService.java
new file mode 100644
index 00000000..5a3ae9ab
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.demo.service;
+
+import ru.otus.demo.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonServiceImpl.java b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonServiceImpl.java
new file mode 100644
index 00000000..29240b6e
--- /dev/null
+++ b/2022-02/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonServiceImpl.java
@@ -0,0 +1,17 @@
+package ru.otus.demo.service;
+
+import ru.otus.demo.dao.PersonDao;
+import ru.otus.demo.domain.Person;
+
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2022-02/spring-04-aop/aop-demo/pom.xml b/2022-02/spring-04-aop/aop-demo/pom.xml
index 2da9b147..950b1292 100644
--- a/2022-02/spring-04-aop/aop-demo/pom.xml
+++ b/2022-02/spring-04-aop/aop-demo/pom.xml
@@ -13,6 +13,7 @@
aop-ctw
aop-ctw-plain
+ aop-custom
aop-ltw
aop-native