- 스프링 bean 등록하기 AOP2024년 12월 29일
- chantleman
- 작성자
- 2024.12.29.:20
가볍게 시작!!
Bar.java
package com.sumin.just.test3; public class Bar { public void init(){ System.out.println("Bar 초기화!!"); } public void cleanup(){ System.out.println("Bar 제거!!"); } }
Baz.java
package com.sumin.just.test3; public class Baz { private Baz baz; private Bar bar; public Baz() { } public Baz getBaz() { return baz; } public void setBaz(Baz baz) { this.baz = baz; } public Bar getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } }
Foo.java
package com.sumin.just.test3; public class Foo { private Baz baz; private Bar bar; private String name; public Baz getBaz() { return baz; } public void setBaz(Baz baz) { this.baz = baz; } public Bar getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <bean name="foo" class="com.sumin.just.test3.Foo" > <property name="bar" ref="bar"/> <property name="baz" ref="baz"/> </bean> <bean name="bar" class="com.sumin.just.test3.Bar"></bean> <bean name="baz" class="com.sumin.just.test3.Baz"></bean> </beans>
FooTest.java
package com.sumin.just.test3; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class FooTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"com/sumin/just/test3/beans.xml"}); Foo foo = (Foo)context.getBean("foo"); Foo foo1 = (Foo)context.getBean("foo"); System.out.println(foo==foo1); foo.setName("홍길동"); System.out.println(foo); System.out.println(foo1); System.out.println(foo.getName()); System.out.println(foo1.getName()); } }
Foo foo = new Foo(); 처럼 일반적인 객체 생성방법으로 생성하게 되면 객체가 하나 더 생성된다
하지만 스프링에서는 new를 하지 않고 getBean 메소드를 사용한다
왜냐하면 스프링은 싱글톤 패턴이기 때문!
beans.xml에서 scope을 따로 지정해주지 않았으므로 싱글톤 scope을 갖게 되면서 어플리케이션이 시작할 때 생성되는 하나의 인스턴스를 쓰기때문에 foo와 foo1은 주소값이 같다는 결론이 나온당
그러므로 결과값은 당연히 true!
하지만 그렇게되면 foo의 값을 변경했을때 foo1도 같이 변경되게 된당
즉 멀티스레드 환경에서는
싱글톤 객체의 프로퍼티값이 여러 스레드에 의해 변경되게 되면서
다른 스레드 입장에서는 예상치 못한 결과값이 나올 수 있게 된다
그럴때는 beans.xml을 아래처럼 scope를 prototype으로 지정하면
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <bean name="foo" class="com.sumin.just.test3.Foo" scope="prototype"> <property name="bar" ref="bar"/> <property name="baz" ref="baz"/> </bean> <bean name="bar" class="com.sumin.just.test3.Bar"></bean> <bean name="baz" class="com.sumin.just.test3.Baz"></bean> </beans>
FooTest.java를 다시 실행했을때 둘은 다른 객체가 된다
※ 이전 글 코드 참고
저번에 했었던 코드를 가져와서
위에서 했던것처럼 bean에 등록해보장 !
beans-manager.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <bean name="manager" class="com.sumin.just.test3.Manager2"></bean> </beans>
1. 쓰고싶은 클래스를 bean xml에 등록한당
Manager2Test.java
package com.sumin.just.test3; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.sumin.just.test2.People2; import com.sumin.just.test2.Student2; public class Manager2Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"com/sumin/just/test3/beans-manager.xml"}); Manager2 manager = (Manager2)context.getBean("manager"); manager.printPeopleList(); } }
2. 그 클래스를 불러오고 싶은 클래스에 ApplicationContext로 xml파일을 가져온당
3. xml파일에서 내가 갖고오고싶은 bean을 getBean()으로 불러오면 됨!
→ 객체 생성과 의존성 주입(Dependency Injection)
4. getBean()으로 생성한 객체를 이용해서 필요한 메소드를 호출한당
Advice4XML.java
package com.sumin.just.test3.advice; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Advice4XML { private static final Logger LOGGER = LoggerFactory.getLogger(Advice4XML.class); //타겟 메서드 실행전 수행 public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); LOGGER.info("beforMethod 실행 "+className + "."+methodName); } //정상적으로 동작이 된 경우 실행됨 public void afterReturningMethod(JoinPoint joinPoint, Object retVal){ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); Object[] args= joinPoint.getArgs(); for(Object arg:args){ LOGGER.info("arg: "+arg); } LOGGER.info("afterReturningMethod 실행 "+className + "."+methodName +", return value="+retVal); } //실행전 처리, 타겟 메서드 직접호출, 예외처리 가능 public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); LOGGER.info(className + "."+methodName+" start !!"); Object[] args= joinPoint.getArgs(); for(Object arg:args){ LOGGER.info("arg: "+arg); } long startTime = System.currentTimeMillis(); //이부분이 핵심로직이 실행되는 것을 대체 하는 라인 Object retVal = joinPoint.proceed(); long endTime = System.currentTimeMillis(); LOGGER.info(className + "."+methodName+" end !!" +", return value="+retVal + " ,lead time = "+(endTime-startTime) +"ms"); return retVal; } //예외가 발생하고, 종료하는 경우 수행됨 public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) throws Throwable{ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); System.err.println(className + "."+methodName+" exception: "+ex.getMessage()); } }
beans-aspect.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <aop:config proxy-target-class="true"> <aop:pointcut id="serviceMethod" expression="execution(* com.sumin.just.test3..*.*(..))" /> <aop:aspect ref="advice4XML"> <aop:around pointcut-ref="serviceMethod" method="aroundMethod"/> </aop:aspect> </aop:config> <bean id="advice4XML" class="com.sumin.just.test3.advice.Advice4XML"/> <bean id="manager" class="com.sumin.just.test3.Manager2"/> </beans>
proxy-target-class가
true면 대상 클래스를 상속받아 proxy 생성(인터페이스 없어도 됨. 클래스가 final인 경우 proxy 생성 불가능)
false면 인터페이스 방식으로 적용(인터페이스로 구현되어있지 않다면 AOP 적용하지 않음 )
pointcut을 설정! (pointcut id와 pointcut-ref / aspect ref와 bean id를 잘 맞춰주자)
우리가 맞춰준 것은 advice4XML의 aroundMethod를 사용하겠다는 의미이당
Manager2Test.java
package com.sumin.just.test3; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.sumin.just.test2.People2; import com.sumin.just.test2.Student2; public class Manager2Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"com/sumin/just/test3/beans-aspect.xml"}); Manager2 manager = (Manager2)context.getBean("manager"); manager.printPeopleList(); People2 people = new Student2(); people.setName("수지"); People2 findPeople=manager.retrieve(people); System.out.println("findPeople: "+findPeople); people = new Student2("3","1","11","수지","25","여자"); int cnt = manager.update(people); System.out.println("cnt: "+cnt); findPeople=manager.retrieve(people); System.out.println("findPeople: "+findPeople); System.out.println(manager.delete(people)); manager.printPeopleList(); } }
advice4xml의 aroundmethod 잘 작동되는 것 확인!
어노테이션 사용해보기
Manager2.java (service)
@Service 추가
package com.sumin.just.test3; @Service("manager") public class Manager2 { private List<People2> peopleList = new ArrayList<People2>(); ... //manager2 소스코드 }
beans-aspect2.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" > <context:component-scan base-package="com.sumin.just.test3"/> <aop:aspectj-autoproxy/> </beans>
아까는 xml에서 타겟 클래스와 메소드를 직접 지정해줬지만
여기서는 aop:aspectj-autoproxy를 선언하여 aop 관련 어노테이션을 자동으로 인식하게 해준다
Advice4XML.java (repository, dao)
@Aspect, @Repository, @Around 추가
package com.sumin.just.test3.advice; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; @Aspect @Repository public class Advice4XML { private static final Logger LOGGER = LoggerFactory.getLogger(Advice4XML.class); //타겟 메서드 실행전 수행 public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); LOGGER.info("beforMethod 실행 "+className + "."+methodName); } //정상적으로 동작이 된 경우 실행됨 public void afterReturningMethod(JoinPoint joinPoint, Object retVal){ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); Object[] args= joinPoint.getArgs(); for(Object arg:args){ LOGGER.info("arg: "+arg); } LOGGER.info("afterReturningMethod 실행 "+className + "."+methodName +", return value="+retVal); } @Around("execution(* com.sumin.just.test3..*.*(..))") public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); LOGGER.info(className + "."+methodName+" start !!"); Object[] args= joinPoint.getArgs(); for(Object arg:args){ LOGGER.info("arg: "+arg); } long startTime = System.currentTimeMillis(); //이부분이 핵심로직이 실행되는 것을 대체 하는 라인 Object retVal = joinPoint.proceed(); long endTime = System.currentTimeMillis(); LOGGER.info(className + "."+methodName+" end !!" +", return value="+retVal + " ,lead time = "+(endTime-startTime) +"ms"); return retVal; } //예외가 발생하고, 종료하는 경우 수행됨 public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) throws Throwable{ String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getName(); System.err.println(className + "."+methodName+" exception: "+ex.getMessage()); } }
Aspect 어노테이션을 붙이면 스프링에서 AOP를 구현했다고 인지하고 proxy를 자동으로 생성해준당
아까 beans-asepct.xml에서 execution 설정한 것을
여기서는 Around 어노테이션을 사용하여 pointcut을 지정한것이당
위와 같이 설정하면 repository(Advice4XML)와 service(Manager2) 둘 다 AOP 적용 대상이 된다
Around 어노테이션은 지정된 패턴에 해당하는 메소드의 실행되기 전, 후 모두에서 동작하며 이 어노테이션이 붙은 메소드의 반환 값은 Object여야한다
어노테이션 안에는 특정 패턴을 지정한 문자열이 들어가고 이를 pointcut이라고 한당
pointcut 표현식
=> * : 모든 값
=> .. : 0개 이상
execution이란 제일 기본적인 방법으로 특정 메소드를 지정하는 패턴을 작성할 수 있는 방법이다
아래와 같은 형태로 특정 메소드까지의 패턴을 딱 지정하는 표현식이다
execution([접근 제어자] 반환타입.클래스.메소드(인자))
Manager2Test.java (controller)
package com.sumin.just.test3; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.sumin.just.test2.People2; import com.sumin.just.test2.Student2; public class Manager2Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"com/sumin/just/test3/beans-aspect2.xml"}); Manager2 manager = (Manager2)context.getBean("manager"); manager.printPeopleList(); People2 people = new Student2(); people.setName("수지"); People2 findPeople=manager.retrieve(people); System.out.println("findPeople: "+findPeople); people = new Student2("3","1","11","수지","25","여자"); int cnt = manager.update(people); System.out.println("cnt: "+cnt); findPeople=manager.retrieve(people); System.out.println("findPeople: "+findPeople); System.out.println(manager.delete(people)); manager.printPeopleList(); } }
아까와 같은 결과가 나온것 확인!
728x90'spring' 카테고리의 다른 글
react, spring boot 연동 (0) 2024.11.26 websocket (0) 2024.11.18 파일업로드 (0) 2024.11.14 @RequestParam, @RequestBody, @ResponseBody (6) 2024.11.12 spring 설치 및 초기 설정 (3) 2024.11.08 다음글이전글이전 글이 없습니다.댓글