diff --git a/2026-01/spring-04-aop/aop-classwork/.gitignore b/2026-01/spring-04-aop/aop-classwork/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/.gitignore
@@ -0,0 +1,24 @@
+target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/pom.xml b/2026-01/spring-04-aop/aop-classwork/exercise/pom.xml
new file mode 100644
index 00000000..a4ffe8f7
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/pom.xml
@@ -0,0 +1,42 @@
+
+
+ 4.0.0
+
+ ru.otus
+ exercise
+ 1.0
+
+
+ 17
+ 17
+ 6.2.11
+
+ 1.9.19
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/Main.java b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..dcac9032
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,22 @@
+package ru.otus.spring;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.service.PersonService;
+
+@Configuration
+@ComponentScan
+public class Main {
+
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(Main.class);
+
+ PersonService service = context.getBean(PersonService.class);
+
+ Person ivan = service.getByName("Ivan");
+ System.out.println("name: " + ivan.name() + " age: " + ivan.age());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/dao/PersonDao.java b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..b39e82b5
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,13 @@
+package ru.otus.spring.dao;
+
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+@Repository
+public class PersonDaoSimple implements PersonDao {
+
+ @Override
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/domain/Person.java b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..9ba649d1
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.domain;
+
+public record Person(String name, int age) {
+
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/logging/LoggingAspect.java b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/logging/LoggingAspect.java
new file mode 100644
index 00000000..e066196d
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/logging/LoggingAspect.java
@@ -0,0 +1,14 @@
+package ru.otus.spring.logging;
+
+import org.aspectj.lang.JoinPoint;
+
+
+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());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/service/PersonService.java b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..e6ee475f
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/exercise/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.service;
+
+import org.springframework.stereotype.Service;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@Service
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ @Override
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/pom.xml b/2026-01/spring-04-aop/aop-classwork/pom.xml
new file mode 100644
index 00000000..2f9b1745
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-classwork
+ 1.0
+
+ pom
+
+
+ exercise
+ solution
+
+
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/pom.xml b/2026-01/spring-04-aop/aop-classwork/solution/pom.xml
new file mode 100644
index 00000000..02f4bb95
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+ ru.otus
+ solution
+ 1.0
+
+
+ 17
+ 17
+ 6.0.9
+
+ 1.9.19
+
+ UTF-8
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/Main.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..ecb4fa44
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,24 @@
+package ru.otus.spring;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.service.PersonService;
+
+@EnableAspectJAutoProxy
+@Configuration
+@ComponentScan
+public class Main {
+
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(Main.class);
+
+ PersonService service = context.getBean(PersonService.class);
+
+ Person ivan = service.getByName("Ivan");
+ System.out.println("name: " + ivan.name() + " age: " + ivan.age());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/dao/PersonDao.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..b51dedad
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,15 @@
+package ru.otus.spring.dao;
+
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.logging.LogMe;
+
+@Repository
+public class PersonDaoSimple implements PersonDao {
+
+ @Override
+ @LogMe
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/domain/Person.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..9ba649d1
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.domain;
+
+public record Person(String name, int age) {
+
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/LogMe.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/LogMe.java
new file mode 100644
index 00000000..cc2ef8c4
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/LogMe.java
@@ -0,0 +1,11 @@
+package ru.otus.spring.logging;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LogMe {
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/Logging2Aspect.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/Logging2Aspect.java
new file mode 100644
index 00000000..ecf4a6f2
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/Logging2Aspect.java
@@ -0,0 +1,21 @@
+package ru.otus.spring.logging;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Order(1)
+@Component
+public class Logging2Aspect {
+
+ @Before("@annotation(ru.otus.spring.logging.LogMe)")
+ public void logBefore(JoinPoint joinPoint) {
+ System.out.println("Прокси2 : " + joinPoint.getThis().getClass().getName());
+ System.out.println("Класс2 : " + joinPoint.getTarget().getClass().getName());
+
+ System.out.println("Вызов метода2 : " + joinPoint.getSignature().getName());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/LoggingAspect.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/LoggingAspect.java
new file mode 100644
index 00000000..4da7fce8
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/logging/LoggingAspect.java
@@ -0,0 +1,19 @@
+package ru.otus.spring.logging;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Component
+public class LoggingAspect {
+
+ @Before("@annotation(ru.otus.spring.logging.LogMe)")
+ 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());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/service/PersonService.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..e6ee475f
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-classwork/solution/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.service;
+
+import org.springframework.stereotype.Service;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@Service
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ @Override
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/.gitignore b/2026-01/spring-04-aop/aop-demo/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/.gitignore
@@ -0,0 +1,24 @@
+target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/exec.sh b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/exec.sh
new file mode 100644
index 00000000..4af59274
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/exec.sh
@@ -0,0 +1,3 @@
+mvn clean package
+java -jar target/aop-ctw-plain-1.0-jar-with-dependencies.jar
+read -p "Press enter to continue"
\ No newline at end of file
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/pom.xml b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/pom.xml
new file mode 100644
index 00000000..b28feca4
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/pom.xml
@@ -0,0 +1,93 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-ctw-plain
+ 1.0
+
+
+ UTF-8
+ 17
+ 17
+ 1.9.19
+ 1.13.1
+
+
+
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+ ${aspectj.plugin.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ jar-with-dependencies
+
+
+
+ ru.otus.spring.Main
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+ ${aspectj.plugin.version}
+
+ 17
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ true
+ true
+ ignore
+ UTF-8
+
+ ${project.build.directory}/classes
+
+ true
+
+
+
+
+ compile
+
+
+
+
+
+
+
+
+
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/Main.java b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..5df2177f
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,25 @@
+package ru.otus.spring;
+
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.dao.PersonDaoSimple;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.service.PersonService;
+import ru.otus.spring.service.PersonServiceImpl;
+
+/*
+Запуск примера:
+ 1. cd aop-demo/aop-ctw-plain/
+ 2. mvn clean package
+ 3. java -jar target/aop-ctw-plain-1.0-jar-with-dependencies.jar
+ */
+
+public class Main {
+
+ public static void main(String[] args) {
+ PersonDao personDao = new PersonDaoSimple();
+ PersonService service = new PersonServiceImpl(personDao);
+
+ Person ivan = service.getByName("Ivan");
+ System.out.println("name: " + ivan.name() + " age: " + ivan.age());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/dao/PersonDao.java b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..8f98abd0
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,11 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public class PersonDaoSimple implements PersonDao {
+
+ @Override
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/domain/Person.java b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..9ba649d1
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.domain;
+
+public record Person(String name, int age) {
+
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/logging/LoggingAspect.java b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/logging/LoggingAspect.java
new file mode 100644
index 00000000..e77adb73
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/logging/LoggingAspect.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.logging;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+
+@Aspect
+public class LoggingAspect {
+
+ @Before("execution(* ru.otus.spring.dao.PersonDaoSimple.*(..))")
+ 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());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/service/PersonService.java b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..d4968d49
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw-plain/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,18 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ @Override
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/exec.sh b/2026-01/spring-04-aop/aop-demo/aop-ctw/exec.sh
new file mode 100644
index 00000000..347ebc16
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/exec.sh
@@ -0,0 +1,3 @@
+mvn clean package
+java -jar target/aop-ctw-1.0-jar-with-dependencies.jar
+read -p "Press enter to continue"
\ No newline at end of file
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/pom.xml b/2026-01/spring-04-aop/aop-demo/aop-ctw/pom.xml
new file mode 100644
index 00000000..ceecf353
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/pom.xml
@@ -0,0 +1,104 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-ctw
+ 1.0
+
+
+ UTF-8
+ 11
+ 11
+ 6.0.9
+ 1.9.19
+ 1.13.1
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+ ${aspectj.plugin.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ jar-with-dependencies
+
+
+
+ ru.otus.spring.Main
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+ ${aspectj.plugin.version}
+
+ 17
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ true
+ true
+ ignore
+ UTF-8
+
+ ${project.build.directory}/classes
+
+ true
+
+
+
+
+ compile
+
+
+
+
+
+
+
+
+
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/Main.java b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..8a9075fc
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,29 @@
+package ru.otus.spring;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.service.PersonService;
+
+/*
+Запуск примера:
+ 1. cd aop-demo/aop-ctw/
+ 2. mvn clean package
+ 3. java -jar target/aop-ctw-1.0-jar-with-dependencies.jar
+ */
+
+@Configuration
+@ComponentScan
+public class Main {
+
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(Main.class);
+
+ PersonService service = context.getBean(PersonService.class);
+
+ Person ivan = service.getByName("Ivan");
+ System.out.println("name: " + ivan.getName() + " age: " + ivan.getAge());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/dao/PersonDao.java b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..b39e82b5
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,13 @@
+package ru.otus.spring.dao;
+
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+@Repository
+public class PersonDaoSimple implements PersonDao {
+
+ @Override
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/domain/Person.java b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..c23be0c6
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.domain;
+
+public class Person {
+
+ private final String name;
+ private final 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/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/logging/LoggingAspect.java b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/logging/LoggingAspect.java
new file mode 100644
index 00000000..e77adb73
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/logging/LoggingAspect.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.logging;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+
+@Aspect
+public class LoggingAspect {
+
+ @Before("execution(* ru.otus.spring.dao.PersonDaoSimple.*(..))")
+ 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());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/service/PersonService.java b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..e6ee475f
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ctw/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.service;
+
+import org.springframework.stereotype.Service;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@Service
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ @Override
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-custom/pom.xml b/2026-01/spring-04-aop/aop-demo/aop-custom/pom.xml
new file mode 100644
index 00000000..26654a5c
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-custom/pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-custom
+ 1.0
+
+
+ UTF-8
+ 17
+ 17
+ 6.0.9
+ 1.9.19
+
+
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+
diff --git a/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/Main.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/Main.java
new file mode 100644
index 00000000..0e1122f5
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/Main.java
@@ -0,0 +1,69 @@
+package ru.otus.demo;
+
+import ru.otus.demo.aop.LoggingAspect;
+import ru.otus.demo.aop.SimpleWeaver;
+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 java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Main {
+
+ public static void main(String[] args) {
+
+ // Как будто Bean Definitions
+ List> beanDefinitions = List.of(SimpleWeaver.class, LoggingAspect.class,
+ PersonDaoSimple.class, BookDaoSimple.class);
+
+ // Как будто контекст
+ Map, Object> context = new HashMap<>();
+ beanDefinitions.stream()
+ // Создание бинов
+ .map(Main::createInstance)
+ // Постобработка, вивинг
+ .forEach(bean -> putBeanIntoContextAndApplyAspectIfNecessary(bean, context));
+
+ // Как будто достаем из контекста дао (уже с примененным аспектом)
+ PersonDao personDaoProxy = (PersonDao) context.get(PersonDao.class);
+ BookDao bookDaoProxy = (BookDao) context.get(BookDao.class);
+
+ // Используем сервисы
+ personDaoProxy.findByName("Вася");
+ System.out.println("-------------------------------------------------");
+ bookDaoProxy.findByTitle("Укротители велосипедов");
+ }
+
+ private static Object createInstance(Class> clazz) {
+ try {
+ return clazz.getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Class> getInterfaceOrClass(Object o) {
+ if (o.getClass().getInterfaces().length > 0) {
+ return o.getClass().getInterfaces()[0];
+ }
+ return o.getClass();
+ }
+
+ private static void putBeanIntoContextAndApplyAspectIfNecessary(Object bean, Map, Object> context) {
+ // Если нет интерфейсов, то это наш вивер и аспект. Просто кладем в контекст
+ // (сам спринг естественно не такие штуки ориентируется, но у нас будет такое допущение)
+ Class> key = getInterfaceOrClass(bean);
+ if (!key.isInterface()) {
+ context.put(key, bean);
+ return;
+ }
+
+ // Если интерфейсы есть, то это наши дао и мы их вивим с аспектом. Результат кладем в контекст
+ SimpleWeaver weaver = (SimpleWeaver) context.get(SimpleWeaver.class);
+ LoggingAspect aspect = (LoggingAspect) context.get(LoggingAspect.class);
+ context.put(key, weaver.weave(bean, aspect));
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/MainSimple.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/MainSimple.java
new file mode 100644
index 00000000..e24ab0b7
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/MainSimple.java
@@ -0,0 +1,67 @@
+package ru.otus.demo;
+
+import org.springframework.cglib.proxy.Enhancer;
+import ru.otus.demo.dao.PersonDao;
+import ru.otus.demo.dao.PersonDaoSimple;
+import ru.otus.demo.service.PersonService;
+import ru.otus.demo.service.PersonServiceImpl;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class MainSimple {
+
+ public static void main(String[] args) {
+
+ LoggingAspectSimple aspect = new LoggingAspectSimple();
+
+ PersonDao personDaoSimple = new PersonDaoSimple();
+
+ // JdkProxy
+ System.out.println("JdkProxy");
+ PersonDao personDaoJdkProxy = (PersonDao) Proxy.newProxyInstance(
+ MainSimple.class.getClassLoader(),
+ personDaoSimple.getClass().getInterfaces(),
+ new java.lang.reflect.InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] methodArgs) throws Throwable {
+ aspect.logBefore(personDaoSimple.getClass(), proxy.getClass(), method, methodArgs);
+ return method.invoke(personDaoSimple, methodArgs);
+ }
+ }
+ );
+ PersonService personService = new PersonServiceImpl(personDaoJdkProxy);
+ personService.getByName("Вася");
+
+
+ // CglibProxy (Spring edition, работает в Java 17 без плясок)
+ System.out.println("\n\nCglibProxy (Spring edition)");
+ Enhancer enhancer = new Enhancer();
+ enhancer.setSuperclass(PersonDaoSimple.class);
+ enhancer.setCallback(new org.springframework.cglib.proxy.InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] methodArgs) throws Throwable {
+ aspect.logBefore(personDaoSimple.getClass(), proxy.getClass(), method, methodArgs);
+ return method.invoke(personDaoSimple, methodArgs);
+ }
+ });
+ PersonDao personDaoCgLibProxy = (PersonDao) enhancer.create();
+ personService = new PersonServiceImpl(personDaoCgLibProxy);
+ personService.getByName("Игорь");
+ }
+
+ private static class LoggingAspectSimple {
+ public void logBefore(Class> originalClass, Class> proxyClass, Method method, Object[] methodArgs) {
+ System.out.println("Прокси : " + proxyClass.getName());
+ System.out.println("Класс : " + originalClass.getName());
+
+ System.out.println("Вызов метода : " + method.getName());
+ System.out.println("Аргументы метода : " + Arrays.stream(methodArgs)
+ .map(Objects::toString)
+ .collect(Collectors.joining(", ")));
+ }
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/LoggingAspect.java b/2026-01/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/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleAOPInvocationHandler.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleAOPInvocationHandler.java
new file mode 100644
index 00000000..c95cd290
--- /dev/null
+++ b/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleJoinPoint.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleJoinPoint.java
new file mode 100644
index 00000000..26260948
--- /dev/null
+++ b/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleSignature.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleSignature.java
new file mode 100644
index 00000000..9699e595
--- /dev/null
+++ b/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleWeaver.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/aop/SimpleWeaver.java
new file mode 100644
index 00000000..4b3a64a6
--- /dev/null
+++ b/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDao.java b/2026-01/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/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDaoSimple.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/BookDaoSimple.java
new file mode 100644
index 00000000..9c039121
--- /dev/null
+++ b/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDao.java b/2026-01/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/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDaoSimple.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..cca909a9
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/dao/PersonDaoSimple.java
@@ -0,0 +1,12 @@
+package ru.otus.demo.dao;
+
+import ru.otus.demo.domain.Person;
+
+
+public class PersonDaoSimple implements PersonDao {
+
+ @Override
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Book.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Book.java
new file mode 100644
index 00000000..25c096a8
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Book.java
@@ -0,0 +1,5 @@
+package ru.otus.demo.domain;
+
+public record Book(String title) {
+
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Person.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Person.java
new file mode 100644
index 00000000..6726c868
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/domain/Person.java
@@ -0,0 +1,12 @@
+package ru.otus.demo.domain;
+
+public record Person(String name, int age) {
+
+ @Override
+ public String toString() {
+ return "Person{" +
+ "name='" + name + '\'' +
+ ", age=" + age +
+ '}';
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookService.java b/2026-01/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/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookServiceImpl.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/BookServiceImpl.java
new file mode 100644
index 00000000..836572ce
--- /dev/null
+++ b/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonService.java b/2026-01/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/2026-01/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/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonServiceImpl.java b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonServiceImpl.java
new file mode 100644
index 00000000..b15ab470
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-custom/src/main/java/ru/otus/demo/service/PersonServiceImpl.java
@@ -0,0 +1,18 @@
+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;
+ }
+
+ @Override
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/exec.sh b/2026-01/spring-04-aop/aop-demo/aop-ltw/exec.sh
new file mode 100644
index 00000000..481a1b67
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/exec.sh
@@ -0,0 +1,3 @@
+mvn clean package
+java -javaagent:c:/Users/User/.m2/repository/org/aspectj/aspectjweaver/1.9.19/aspectjweaver-1.9.19.jar -jar target/aop-ltw-1.0-jar-with-dependencies.jar
+read -p "Press enter to continue"
\ No newline at end of file
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/pom.xml b/2026-01/spring-04-aop/aop-demo/aop-ltw/pom.xml
new file mode 100644
index 00000000..76886f9d
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/pom.xml
@@ -0,0 +1,80 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-ltw
+ 1.0
+
+
+ UTF-8
+ 17
+ 17
+ 6.0.9
+ 1.9.19
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-instrument
+ ${spring.version}
+
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ jar-with-dependencies
+
+
+
+ ru.otus.spring.Main
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/Main.java b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..2e5103d9
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,31 @@
+package ru.otus.spring;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.service.PersonService;
+
+/*
+Запуск примера:
+ 1. В IDEA, вменю запуска выбираем "Edit Configurations..."
+ 2. В поле "VM options" вносим "-javaagent:${путь.до.m2}\.m2\repository\org\aspectj\aspectjweaver\1.9.8\aspectjweaver-1.9.8.jar",
+ где ${путь.до.m2} это путь до репозитория мавен на текущем компьютере.
+ Пример: "-javaagent:c:\Users\MyUserName\.m2\repository\org\aspectj\aspectjweaver\1.9.9.1\aspectjweaver-1.9.9.1.jar".
+ Кавычки не вносим)
+ 3. Запускаем Main
+*/
+
+@Configuration
+@ComponentScan
+public class Main {
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(Main.class);
+
+ PersonService service = context.getBean(PersonService.class);
+
+ Person ivan = service.getByName("Ivan");
+ System.out.println("name: " + ivan.name() + " age: " + ivan.age());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/dao/PersonDao.java b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..8ffbcf41
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,16 @@
+package ru.otus.spring.dao;
+
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+@Repository
+public class PersonDaoSimple implements PersonDao {
+ public PersonDaoSimple() {
+ System.out.println(this.getClass().getClassLoader());
+ }
+
+ @Override
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/domain/Person.java b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..9ba649d1
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.domain;
+
+public record Person(String name, int age) {
+
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/logging/LoggingAspect.java b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/logging/LoggingAspect.java
new file mode 100644
index 00000000..e77adb73
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/logging/LoggingAspect.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.logging;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+
+@Aspect
+public class LoggingAspect {
+
+ @Before("execution(* ru.otus.spring.dao.PersonDaoSimple.*(..))")
+ 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());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/service/PersonService.java b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..e6ee475f
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.service;
+
+import org.springframework.stereotype.Service;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@Service
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ @Override
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/resources/META-INF/aop.xml b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/resources/META-INF/aop.xml
new file mode 100644
index 00000000..adf0ac99
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-ltw/src/main/resources/META-INF/aop.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/pom.xml b/2026-01/spring-04-aop/aop-demo/aop-native/pom.xml
new file mode 100644
index 00000000..e967f24d
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/pom.xml
@@ -0,0 +1,42 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-native
+ 1.0
+
+
+ UTF-8
+ 17
+ 17
+ 6.0.9
+ 1.9.19
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjweaver
+ ${aspectj.version}
+
+
+
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/Main.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..dcac9032
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,22 @@
+package ru.otus.spring;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.service.PersonService;
+
+@Configuration
+@ComponentScan
+public class Main {
+
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(Main.class);
+
+ PersonService service = context.getBean(PersonService.class);
+
+ Person ivan = service.getByName("Ivan");
+ System.out.println("name: " + ivan.name() + " age: " + ivan.age());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/config/CommonNativeAopConfig.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/config/CommonNativeAopConfig.java
new file mode 100644
index 00000000..cb9f6bc2
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/config/CommonNativeAopConfig.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.config;
+
+import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
+
+import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;
+
+@Role(ROLE_INFRASTRUCTURE)
+@Configuration
+public class CommonNativeAopConfig {
+ @Bean
+ public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
+ return new DefaultAdvisorAutoProxyCreator();
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/config/LoggingAspectConfig.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/config/LoggingAspectConfig.java
new file mode 100644
index 00000000..20d79ce9
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/config/LoggingAspectConfig.java
@@ -0,0 +1,39 @@
+package ru.otus.spring.config;
+
+import org.springframework.aop.ClassFilter;
+import org.springframework.aop.MethodBeforeAdvice;
+import org.springframework.aop.MethodMatcher;
+import org.springframework.aop.Pointcut;
+import org.springframework.aop.support.DefaultPointcutAdvisor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import ru.otus.spring.dao.PersonDaoSimple;
+
+@Configuration
+public class LoggingAspectConfig {
+
+ public Pointcut personDaoSimpleLoggingAspectPointcut() {
+ return new Pointcut() {
+ @Override
+ public ClassFilter getClassFilter() {
+ return clazz -> clazz.equals(PersonDaoSimple.class);
+ }
+
+ @Override
+ public MethodMatcher getMethodMatcher() {
+ return MethodMatcher.TRUE;
+ }
+ };
+ }
+
+ MethodBeforeAdvice personDaoSimpleLoggingAspectBeforeAdvice() {
+ return (method, objects, o) -> System.out.println("Ура! Вызов метода : " + method.getName());
+ }
+
+
+ @Bean
+ public DefaultPointcutAdvisor personDaoSimpleLoggingAspectAdvisor() {
+ return new DefaultPointcutAdvisor(personDaoSimpleLoggingAspectPointcut(),
+ personDaoSimpleLoggingAspectBeforeAdvice());
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/dao/PersonDao.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..b39e82b5
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,13 @@
+package ru.otus.spring.dao;
+
+import org.springframework.stereotype.Repository;
+import ru.otus.spring.domain.Person;
+
+@Repository
+public class PersonDaoSimple implements PersonDao {
+
+ @Override
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/domain/Person.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..9ba649d1
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.domain;
+
+public record Person(String name, int age) {
+
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/service/PersonService.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..e6ee475f
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/aop-native/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.service;
+
+import org.springframework.stereotype.Service;
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+@Service
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ @Override
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2026-01/spring-04-aop/aop-demo/pom.xml b/2026-01/spring-04-aop/aop-demo/pom.xml
new file mode 100644
index 00000000..8828cdf6
--- /dev/null
+++ b/2026-01/spring-04-aop/aop-demo/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+ ru.otus
+ aop-demo
+ 1.0
+
+ pom
+
+
+ aop-ctw
+ aop-ctw-plain
+ aop-custom
+ aop-ltw
+ aop-native
+
+