사실 작은 규모의 프로젝트에서는 스프링의 DI사용을 하는 것 보다 일반적인 방법을 사용하여 개발하는 것이 더욱 빠르고, 개발에 따른 스트레스를 줄일 수 있습니다. 하지만 규모가 어느 정도 커지고, 추후 유지보수 업무가 발생시에는 DI를 이용한 개발의 장점을 느낄 수 있습니다.
public static void main(String[] args) { // MyCalculator cal = new MyCalculator(); // cal.setFirstNum(10); // cal.setSecondNum(2); // cal.add(); // cal.sub(); // cal.multi(); // cal.divide(); String configLocation = "classpath:applicationCTX.xml"; AbstractApplicationContext ctx = new GenericXmlApplicationContext(configLocation); MyCalculator myCalculator = ctx.getBean("myCalculator", MyCalculator.class); myCalculator.add(); myCalculator.sub(); myCalculator.multi(); myCalculator.divide(); }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Root Context: defines shared resources visible to all other web components --> <bean id="calculator" class="com.taekgu.ex.Calculator"/> <bean id="myCalculator" class="com.taekgu.ex.MyCalculator"> <property name="calculator"> <ref bean="calculator"/> </property> <property name="firstNum" value="10"/> <property name="secondNum" value="2"/> </bean> </beans>
public class Student implements InitializingBean, DisposableBean { ..... @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet()"); } @Override public void destroy() throws Exception { System.out.println("destroy()"); } }
<bean id="otherStudent" class="com.taekgu.ex.OtherStudent" init-method="initMethod" destroy-method="destoryMethod" scope="prototype|singleton"> <constructor-arg value="홍길동"/> <property name="age"> <value>10</value> </property> <property name="hobbys"> <list> <value>Game</value> <value>Reading</value> </list> </property> </bean>
<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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Root Context: defines shared resources visible to all other web components --> <context:property-placeholder location="classpath:admin.properties, classpath:sub_admin.properties" /> <bean id="calculator" class="com.taekgu.ex.Calculator"/> <bean id="myCalculator" class="com.taekgu.ex.MyCalculator"> <property name="calculator"> <ref bean="calculator"/> </property> <property name="firstNum" value="10"/> <property name="secondNum" value="2"/> </bean> <bean id="otherStudent" class="com.taekgu.ex.OtherStudent" init-method="initMethod" destroy-method="destoryMethod" scope="prototype"> <constructor-arg value="${adminUserName}"/> <property name="age"> <value>10</value> </property> <property name="hobbys"> <list> <value>Game</value> <value>Reading</value> </list> </property> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" profile="dev"> <!-- Root Context: defines shared resources visible to all other web components --> <bean id="serverInfo" class="com.taekgu.ex.ServerInfo"> <property name="serverAddress" value="localhost"></property> <property name="serverPort" value="8080"></property> </bean> </beans>
public class MainEnv { public static void main(String[] args) { String config = null; Scanner scanner = new Scanner(System.in); String str = scanner.next(); if(str.contentEquals("dev")) { config = "dev"; } else if( str.contentEquals("prod")) { config = "prod"; } scanner.close(); GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); ctx.getEnvironment().setActiveProfiles(config); ctx.load("applicationCTX_dev.xml","applicationCTX_prod.xml"); ServerInfo info = ctx.getBean("serverInfo", ServerInfo.class); System.out.println("serverAddress=" + info.getServerAddress()); System.out.println("serverPort=" + info.getServerPort()); ctx.close(); } }
프로그래밍을 하다 보면, 공통적인 기능이 많이 발생 합니다. 이러한 공통 기능을 모든 모듈에 적용하기 위한 방법으로 상속을 통한 방법이 있습니다. 상속도 좋은 방법이기는 하지만 몇 가지 문제가 있습니다.우선 JAVA에서는 다중 상속이 불가하므로 다양한 모듈에 상속기법을 통한 공통 기능 부여는 한계가 있습니다. 그리고, 기능 구현부분에 핵심 기능 코드와 공통 기능 코드가 섞여 있어 효율성이 떨어집니다.
위의 상속을 통한 방법에 한계가 있어 AOP가 등장하게 되었습니다. AOP방법은 핵심 기능과 공통 기능을 분리 시켜놓고, 공통 기능을 필요로 하는 핵심 기능들에서 사용하는 방식 입니다.
쉽게 생각해서 아침에 밥을 짓는다고 생각해 봅니다. 핵심 기능은 쌀을 씻고, 깨끗한 물을 적당히 넣고, 전자밥솥에 내솥을 넣고, 취사 버튼을 누르는 기능들 일 것입니다 공통 기능은 수도 꼭지를 열어 물을 받고, 쌀이 깨끗이 씻겼는지 눈으로 판단하고, 물을 적당한지 판단하는 기능들 일 것입니다. 이러한 기능이 공통 기능인 것은 밥을 짓는 행동이 아닐 때도 우리는 수도 꼭지를 열고, 눈으로 사물을 보고 적절한 판단을 하기 때문에 공통 기능이라고 하였습니다. 어쨌든, 이렇게 핵심 기능과 공통 기능을 분리해 놓고, 추후에 밥을 짓는 행동 말고 팥을 쑬 때도 핵심 기능은 변화지만, 공통 기능은 다시 적용할 수 있을 것입니다.
AOP 기법이 바로 이러한 것입니다. 공통 기능을 핵심 기능과 분리해 놓고, 공통 기능 중에서 핵심 기능에 적용하고자 하는 부분에 적용하는 것입니다.
Spring에서 AOP구현 방법 : proxy를 이용합니다. 스프링에서 AOP구현 방식
용어정리
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" profile="dev" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- Root Context: defines shared resources visible to all other web components --> <bean id="logAop" class="com.taekgu.ex.LogAop" /> <aop:config> <aop:aspect id="logger" ref="logAop"> <aop:pointcut id="publicMethod" expression="within(com.taekgu.ex.*)" /> <aop:around pointcut-ref="publicMethod" method="loggerAop" /> </aop:aspect> </aop:config> <bean id="serverInfo" class="com.taekgu.ex.ServerInfo"> <property name="serverAddress" value="localhost"></property> <property name="serverPort" value="8080"></property> </bean> </beans>
package com.taekgu.ex; import org.aspectj.lang.ProceedingJoinPoint; public class LogAop { public Object loggerAop(ProceedingJoinPoint joinpoint) throws Throwable { String signatureStr = joinpoint.getSignature().toShortString(); System.out.println(signatureStr + "is start."); long st = System.currentTimeMillis(); try { Object obj = joinpoint.proceed(); return obj; } finally { long et = System.currentTimeMillis(); System.out.println(signatureStr + " is finished."); System.out.println(signatureStr + " 경과시간 " + (et - st)); } } }