본문 바로가기
Today I learned

1월24일 봄이오나봄, Spring 프레임워크

by soheemon 2019. 1. 24.

스프링 들어가기 전..

스프링은 강의 교재가 잘되어 있습니다. 신입사원이 알아야 할 내용이 전부 들어가 있다.

실무에서는 3버전 쓴다.

<! 이번에 사용하는 방식은 오래된 버전인데, SI에서는 이 버전을 사용합니다. >

스프링이 나온 이유

 EJB 방식이 비효율적이고 무겁다..

개발하기가 어렵고, 호환성이 없다.

*스프링은 스트럭처럼 단지 MVC를 하기위한 프레임워크가 아니라

*EJB에서 기존에 해왔던 작업을 할 수 있게 하는것이 주 목적이다.


Spring 장점

*경량 컨테이너: 객체의 라이프 사이클 관리


*DI(Dependency Injection 의존성 주입: 객체간의 Relation, 의존관계) 지원

->개발자가 new로 객체를 생성하지 않는다. 관리하지 않아도 된다. Spring이 생성한다.


AOP(Aspect Oriented Programming)지원

=핵심 관심사항에 공통 관심사항을 어떻게 적용시킬 것인가… => 중앙에서 관리 가능! 공통된 부분에 대해 중앙에서 관리. 유지보수/수정이 편리하다.

핵심 관심사항 : 비즈니스 서비스…

공통 관심사항 : 시큐리티, 트랜잭션…


POJO(Plain Old Java Object): 기존에 만들어진 자바 객체 Spring안에서 호환 가능


JDBC를 위한 다양한 API 와의 연동이 된다.


***다양한 API와의 연동 지원

= 모든 기술을 Spring에서 가져와서 연동 할 수 있다. (가장 큰 장점!)


스프링 개발 환경
STS(Sprint Test Suite)를 사용한다. 스프링 개발에 최적화된 이클립스임.
STS를 안쓰는곳도 있습니다. 그냥 이클립스를 쓰는곳도 있음. 대비를 합시다.

Maven사용하기 
프로젝트 - Configure - Convert to Maven Project
Maven
“아파치 소프트웨어 재단에서 개발하는 Java 기반 프로젝트의 라이프사이클 관리를 위한 빌드 도구. 이에 따라 컴파일과 빌드를 동시에 수행, 테스트를 병행하거나 서버 측 Deploy 자원을 관리할 수 있는 환경을 제공한다. 또한 라이브러리 관리 기능도 내포하고 있다. Java로 개발하다 보면 다양한 라이브러리를 필요로 하게 되는데, pom.xml 파일에 필요한 라이브러리만 적으면 Maven이 알아서 다운받고 설치해주고 경로까지 지정해준다.”
=>출처: 위키에서 Maven검색

메이븐 정말좋다. 여러분 메이븐쓰세요
Maven 간단 사용법: 
Maven원격 레파지토리가 있는데. 그곳에 가서 라이브러리를 검색 후,
복사!
pom.xml dependencies 태그 밑에다 붙여넣기!

그러면 관련 라이브러리를 가져오게 되는것입니다.
로컬에도 저장이됩니다!
<!주의: 라이브러리를 가져올때 왠만하면 건드리지 맙시다. 기다림의 미학>
하지만 메이븐 프로젝트에도 오류가 납니다.
그중에 하나는 라이브러리가 충돌되는경우입니다.
문제 해결방법 - HOME/.mv에 가서 repository 들어가세요[이곳이 로컬 레파지토리입니다]
1. 문제가 발생한 dependency 태그를 삭제합니다.
2. 이클립스를 끄고 로컬레파지토리를 삭제합니다. 그리고 다시 이클립스를 켜서 원격에서 라이브러리를 가져옵니다.
3. 그래도 해결이 안되면 최후 보루로 다른 버전을 다운받아 봅시다.

*의존성주입
 객체간의 의존관계를 객체 자신이 아닌 외부의 조립기가 수행된다.

의존성 주입(Dependency Injection, DI)은 프로그래밍에서 구성요소간의 의존 관계가 소스코드 내부가 아닌 외부의 설정파일 등을 통해 정의되게 하는 디자인 패턴 중의 하나이다.

예를들어, Service객체가 생성자에  파라미터로써 DAO라는 객체가 필요할때, 이것을 Relation이 이루어진다고 하고 의존관계라고 한다.
[Service 객체는 DAO가 없으면 돌아가지 않는다.]
이때 문제점은, DAO객체가 다른 객체로 변경해야 할 경우엔 Relation이 이루어진 모든 소스코드를 수정해야 한다는 점이다!
스프링에서는, 스프링컨테이너가 중앙에서 모든 객체를 관리하면서, Service가 특정 객체가 필요하다고 요청을 하면
객체를 주입한다. => 이런 방식을 느슨한 결합이라고 한다.
느슨한 결합은 인터페이스에 의한 의존관계만을 알고있으며, 이 의존관계는 구현 클래스에 대한 차이를 모르는채, 서로 다른 구현으로 대체가 가능하다는 점이다.

*Spring의 DI지원
-Spring Container가 DI조립기를 제공. 스프링 설정파일을 통하여 객체간의 의존관계를 설정한다.
-Spring Container가 제공하는 api를 이용해 객체를 사용 할 수 있다.

*Spring 설정파일

- Application에서 사용할 Spring 자원들을 설정하는 파일

- Spring Container는 설정파일에 설정된 내용을 읽어 Appication에서 필요한 기능들을 제공한다.

- XML기반으로 작성한다. (4점대 이후에서는 JAVA파일도 가능하다.)

- Root Tag는 <beans>다.

- 파일명은 상관없다.


*Bean객체 주입 받기 - 설정파일 설정

- 주입할 객체를 설정파일에 설정한다.

id: 주입받을 곳에서 호출할 이름 설정

class: 주입할 객체의 클래스

<?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-2.5.xsd">


<bean id=“dao” class=“spring.di.model.MemberDAO”/>

</beans>


*Bean객체 실제로 주입 받기

public static void main(String [] args){

//설정파일이 어디 있는지를 저장하는 객체

Resource resource = new ClassPathResource("applicationContext.xml");

//객체를 생성해주는 factory 객체

BeanFactory factory = new XmlBeanFactory(resource);

//설정파일에 설정한 <bean> 태그의 id/name을 통해 객체를 받아온다.

MemberDAO dao = (MemberDAO)factory.getBean("dao");

}


*DI관련 주요 클래스


*명시적 객체선언

//스프링컨테이너에 객체를 등록하기 위해 xml파일에 작성하는 방법을 이용할것이다.

//bean태그로 객체를 등록해준다. class는 객체를 생성할 실제JAVA클래스의 경로를 지정해준다.


1)설정을 통한 객체 주입 예제 - constructor를 이용

package vo;

public class Person{

private String id,

private String name,

private int age;

public Person(String id){...} //1번 생성자

public Person(String id, String name){…} //2번 생성자

public Person(int age){…} //3번 생성자

}


//생성자에 주입

<bean id=“person” class=“vo.Person”>

<constructor-arg>

<value>abcde</value>

</constructor-arg>

</bean>

또는

<bean id=“person” class=“vo.Person”>

<constructor-arg value=“abc”/>

</bean>


public class BusinessService{

private Dao dao = null;

public BusinessService(Dao dao){

this.dao = dao;

}

}


<bean id=“dao” class=“spring.di.model.OracleDAO”/>

<bean id=“service” class=“spring.di.model.service.BusinessService”>

<constructor-arg>

<ref bean = “dao”/>

</constructor-arg>

</bean>

또는

<bean id=“service” class=“spring.di.model.service.BusinessService”>

<constructor-arg ref=“dao”>

</bean>


2) Property를 이용하는 예제


값을 주입 받을 객체 - Primitive Data Type 주입

package spring.vo;

public class Person{

private String id,

private String name,

private int age;

public void setId(String id) {…}

public void setName(String name) {…}

public void setAge(int age) {…}


<bean id=“person” class=“vo.Person”>

<property name="name">

<value>hong</value>

</property>

<property name="id" value="abcde"/>

<property name="age" value="20"/>

</bean>


값을 주입 받을 객체 - Bean 객체 주입

public class BusinessService{

private Dao dao = null;

public setDao(Dao dao){…}

}


<bean id=“dao” class=“spring.di.model.OracleDAO”/>

<bean id=“service” class=“spring.di.model.service.BusinessService”>

<property name=“dao”>

<ref bean =“dao”/>

</property >

</bean>

또는

<bean id=“service” class=“spring.di.model.service.BusinessService”>

< property name=“dao” ref=“dao”>

</bean>

 *Bean 객체의 생성 단위

<bean>의 scope 속성을 이용해 설정

//명시적으로 의존성을 주입한다.

//이때 service객체는 생성자를 이용해서 oracleDao객체를 생성한 후, 이것을 생성시 파라미터로 주입받게 된다.

//이게 무슨 의미냐면,

<?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">

<!-- oracleDao 객체 생성 -->

        <bean id="oracleDao" class="kosta.OracleDao"/>

 

        <!-- dao 객체를 넘기는 서비스 객체 생성자를 이용해서 arg 넘긴다.-->

        <bean id="service" class="kosta.WriteService">

               <constructor-arg ref="oracleDao"/>

        </bean>

</beans>


//constructor-arg방법은 생성자 안에 파라미터를 받는방법.

//property 방법은 setter, getter방식을 사용하게 된다. default생성자 실행 이후 setter로 insect하는 방법.


package kosta;

 

public class WriteService implements Service{

       

        private Dao dao;

        //생성자방식으로 사용

        public WriteService() {}

        public WriteService(Dao dao) {

               this.dao = dao;

        }

       

        public Dao getDao() {

               return dao;

        }

        public void setDao(Dao dao) {

               this.dao = dao;

        }

        @Override

        public void insert() {

               System.out.println("WriteService insert()호출");

               dao.insertBoard();

        }

 

}


객체를 생성하면서 파라미터가 있을때도 있음. 그때는 bean하위 태그로 constructor-arg나 property태그를 사용한다.


*명시적 의존성주입

1) constructor-arg 방식

2) property 방식

<?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">

<!-- oracleDao 객체 생성 -->

        <bean id="oracleDao" class="kosta.OracleDao"/>

        <bean id="mysqlDao" class="kosta.MysqlDao"/>

       

        <!-- 1) dao 객체를 넘기는 서비스 객체 생성자를 이용해서 arg 넘긴다.-->


        <bean id="service" class="kosta.WriteService">

               <constructor-arg ref="mysqlDao"/>

        </bean>

         

         <!-- 2) WriteService 변수에 setter 이용해서 넣기. WriteServicedao setter 있어야 한다. -->


         <bean id="service" class="kosta.WriteService">

                <property name="dao" ref="mysqlDao"></property>

         </bean>

         

</beans>


//실제로 Main에서 사용해보자.

//Resource를 생성한다.(applicationContext.xml 파일은 위에 XML)

//그리고 BeanFactory를 생성 후...

//BeanFactory의 getBean(id명)메서드를 사용해서 객체를 생성한다.


package kosta;

 

import org.springframework.beans.factory.BeanFactory;

import org.springframework.context.support.GenericXmlApplicationContext;

import org.springframework.core.io.ClassPathResource;

import org.springframework.core.io.Resource;

 

public class Main {

 

        public static void main(String[] args) {

               Resource resource =

                              new ClassPathResource("applicationContext.xml");

               BeanFactory factory =

                              new GenericXmlApplicationContext(resource); //스프링 컨테이너 생성

               //writeService안의 insert메서드를 호출하고 싶어..!

              

               //스프링컨테이너에 의해서 의존성주입이 실제로 되는지 확인해보자.

               Service service = (Service) factory.getBean("service");

               service.insert();

        }

 

}



package kosta;

 

public class OracleDao implements Dao {

 

        @Override

        public void insertBoard() {

               System.out.println("Oracle Dao insertBoard() 호출!");

 

        }

 

}



package kosta;

 

public class MysqlDao implements Dao {

 

        @Override

        public void insertBoard() {

               // TODO Auto-generated method stub

               System.out.println("MySqlDao insertBoard()!");

        }

 

}



*묵시적 방법

자동으로 패키지 단위로 객체를 가져오는방식.

<?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"

        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-4.3.xsd">

<!-- oracleDao 객체 생성 -->


        <!-- 패키지 단위로 객체 가져오기. 가져올 클래스 위에 @Repository처럼 annotation해야합니다.-->

<!-- context:component-scan 태그로 객체를 가져옵니다 -->

         <context:annotation-config/>

         <context:component-scan base-package="kosta"/>

         

         <bean id="service" class="kosta.WriteService">

         </bean>

         

</beans>


//context:를 사용하기 전에 Namespace탭에서 context NameSpace를 사용하도록 설정을 해줘야 한다.



// bean태그를 사용할땐 하나하나 생성했지만... 

<context:component-scan/> 태그를 사용하면 패키지단위로 불러 올 수 있다.


하지만, 클래스를 불러오는데에는 한가지 조건이 있는데,

바로 자동으로 불러올 클래스 위에 @annotaion으로 표시해줘야 한다.

예 = @Controller, @service, @Repository(DAO), @Component


자동으로 @annotaion되어있는 클래스를 가져온다☆

package kosta;

 

import org.springframework.stereotype.Repository;

 

@Repository

public class MysqlDao implements Dao {

 

        @Override

        public void insertBoard() {

               // TODO Auto-generated method stub

               System.out.println("MySqlDao insertBoard()!");

        }

        public MysqlDao(){};

}


*묵시적 의존성주입

//하지만 여기까지는 단순히 객체를 생성한거고, 아직 의존성 주입이 안되어있다.

의존성 주입을 위해서, Autowired를 사용한다 (명시적 방식 못씀!)


1) 3점대에서는 @Autowired를 사용한다. - *setter위에 @Autowired표기!

2) 4점대에서는 @Injection을 사용한다. - *id로 알아서 Mapping해주기 때문에 간편! 하지만 이름이 겹치면 큰일남!

아래엔 @Autowired 예이다.

        //자동으로 의존성 주입하기

        //@Autowired  의미는 Dao객체를 자동으로 얻어오기를 원한다는것.!


        //스프링 컨테이너야, Dao 객체 있니?:/

        //! 자동으로 가져왔어요 :D MysqlDao!

       

        //<!주의. 만약 스프링 컨테이너가 Dao객체를 2 가져오면 에러가 납니다 ㅠㅠ expected single matcing..에러..>

        //스프링 4점대에는 Autowired 아니라 Inject을 사용합니다

        @Autowired

        public void setDao(Dao dao) {

               this.dao = dao;

        }


//패키지 단위로 객체를 가져올때,

Main에서는 resource를 명시적 방식과 가져오는것은 동일하지만, 

factory는 ApplicationContext객체를 사용한다.

public static void main(String[] args) {

              

   Resource resourcenew ClassPathResource("applicationContext.xml");    

           //패키지 단위로 가져올때 사용

   ApplicationContext factory = new GenericXmlApplicationContext(resource);

정리하자면!

//자동으로 의존관계 설정하기

1.자동으로 생성할 객체를 class 위에 @controller, @service, @repository을 해야한다.

2. componentScan으로 패키지 내의 객체를 자동으로 가져온다.

3. @autowire나 @inject(4점대, 라이브러리 필요)를 사용해서 의존성을 주입해준다.

<! 이때, 자동으로 넣어야 할 객체를 어떻게 알까? => 데이터 타입을 기준으로 판단한다. 그래서 데이터타입이 겹치면 오류난다.!>

  

*XML방식이 아니라 JAVA에서 스프링컨테이너 사용

스프링컨테이너에서 객체를 관리하는 방법을, 위에서는 XML방식을 사용했지만. 이번에는 JAVA클래스로 사용해봅시다!

Configration역할을 하는 JAVA클래스 위에 @configration이라고 표시를 합니다!

그리고, 관리할 객체를 메서드로 등록하는데, 이때 메서드 이름은 XML방식에서 사용했었던 id명역할을 합니다.

의존성 주입은 마찬가지로 @Autowired나 @Inject로 사용합니다.


package kosta;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

 

//configration역할을 하는 JAVA파일입니다. @configuration으로 표시하죠!

@Configuration

public class Factory {

       

        @Bean

        public Dao OracleDao() {//메서드 이름이 xml id 역할을 합니다!

               return new OracleDao();

        }

       

        //WriteService클래스 내에 Autowired 선언되어 있다면,자동으로 의존성주입이 됩니다.

        @Bean

        public Service service() {

               return new WriteService();

        }

}


//Java Config 방식은 AnnotationConfigApplicationContext 메서드를 호출하고 전달인자는 JAVA파일이름.class이다.

실제로 잘되나 확인해보자.


package kosta;

 

import org.springframework.beans.factory.BeanFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

import org.springframework.core.io.ClassPathResource;

import org.springframework.core.io.Resource;

 

public class Main {

 

        public static void main(String[] args) {

              

               //JAVA Config방식을 사용할때

               ApplicationContext factory =

                              new AnnotationConfigApplicationContext(Factory.class); //factory라는 Class 사용할게

              

               //스프링컨테이너에 의해서 의존성주입이 실제로 되는지 확인해보자.

               Service service = (Service) factory.getBean("service");

               service.insert();

        }

 

}



*Spring AOP(Aspect Oriented Programming

==>설계단계부터 들어가야 한다. 잘 모르면 사용하기 어렵다.ㅠ


핵심관심사항과 공통관심사항으로 나눈다.

핵심관심사항: 업무 로직에 핵심적인 기능. client의 요구사항. CourseService, StudentService...

공통관심사항: 코드에 공통적인 부분. Security, Transactions....


AOP는,

문제를 해결하기 위한 핵심 관심 사항과 전체에 적용되는 

공통 관심사항을 기준으로 프로그래밍 함으로써 공통 모듈을 손쉽게 적용할 수 있게 해준다.


*Spring AOP 용어

공통 관심사항을

Aspect라는 단어라고 부른다. 예>트랜잭션, 로깅, 보안


호출되는 시점을

JoinPoint라고 부른다. 이는 Aspect가 적용 될 수 있는 지점이다. 예>메서드, 필드


Acpect가 적용될 JoinPoint을

PointCut이라고 한다. <PointCut을 어떻게 정하냐에 따라서 JoinPoint가 결정된다.>


어느시점에 어떤 Aspect를 적용할지 정의하는것을

Advice라고 한다. <예: 핵심관심사항 이전에 호출할지/이후에 호출할지 결정한다>


Spring에서 AOP 구현 방법

1)POJO Class를 이용

2)스프링 API

3) Annotaion이용


<aop:config>

<aop:pointcut id=“publicmethod” expression=“execution(public * org.kosta.spring..*(..))”/> -->pointcut 모든메서드.

<aop:aspect id=“logingAspect” ref=“writelog”> -->공통관심사항. 호출할 사항.

<aop:around pointcut-ref=“publicmethod” method=“logging”/> --> 전후로 loggin메서드 호출

</aop:aspect>

</aop:config>


AOP 설정 태그

1. <aop:config> : aop설정의 root 태그. – weaving들의 묶음.

2. <aop:aspect> : Aspect 설정 – 하나의 weaving에 대한 설정

3. <aop:pointcut> : Pointcut 설정

4. Advice 설정태그들

A. <aop:before> - 메소드 실행 전 실행될 Advice

http://edu.kosta.or.kr

B. <aop:after-returning> - 메소드 정상 실행 후 실행될 Advice

C. <aop:after-throwing> - 메소드에서 예외 발생시 실행될 Advice

D. <aop:after> - 메소드 정상 또는 예외 발생 상관없이 실행될 Advice – finally

E. <aop:around> - 모든 시점에서 적용시킬 수 있는 Advice 구현


AOP 간단 예제

//insert()를 호출하면 자동으로 공통 관심사항이 호출된다.


<?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.xsd

               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

              

               <!-- AOP 관련 설정용 xml파일. namespaces에서 aop 추가 준다. -->

              

               <!-- Aspect 메모리에 올라가야 하니까, bean으로 선언 -->

               <bean id="logAspect" class="kosta.LogginAspect"/>

              

               <!-- expression 중요하다. 핵심관심사항을 표현식으로 넣는다.-->

               <aop:config>

                       <aop:pointcut expression="execution(public * kosta..*Service.insert(..))" id="servicePointCut"/>

                       <aop:aspect id="loggingAspect" ref="logAspect">

                              <aop:around method="logging" pointcut-ref="servicePointCut"/>

                       </aop:aspect>

               </aop:config>

 

</beans>

 



package kosta;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.aspectj.lang.ProceedingJoinPoint;

import org.springframework.util.StopWatch;

 

public class LogginAspect {

       

               private Log log = LogFactory.getLog(getClass());

               //공통 관심 메서드 Aspect.

               //언제 => advice : around() => 이전(핵심) 이후

              

               public Object logging (ProceedingJoinPoint joinPonit)throws Throwable {

                       log.info("log start");

                       StopWatch stopWatch = new StopWatch();

                      

                       try {

                              stopWatch.start();

                             

                              //핵심 관심로직 호출

                              Object obj = joinPonit.proceed();//insert() 호출!

                              return obj;

                       } catch (Exception e) {

                             

                              throw e;

                       }finally {

                              stopWatch.stop();

                              log.info("log end");

                              log.info(joinPonit.getSignature().getName() + "메서드 실행시" +

                                              stopWatch.getTotalTimeMillis());

                       }

               }

}

 



public class Main {

 

        public static void main(String[] args) {

              

               //하나는 자바 생성용 XML, 하나는 AOP 관련 XML 두개를 넣지만, 하나로 만들어준다!

               String[] config = {

                              "applicationContext.xml",

                              "commonConcern.xml"

               };

              

               ApplicationContext factory =

                              new ClassPathXmlApplicationContext(config);

              

               Service service = (Service) factory.getBean("service");

               service.insert();

        }

 

}

=> 실행 결과

여기서, 우리가 정한 범위외의 메서드가 호출됐을땐

AOP가호출되면 안된다.


정말로 그런지 테스트 해보자. 

인터페이스 하나를 만들고 insert라는 메서드를 만든다.

인터페이스를 구현하는 클래스를 하나 만든다.

그 클래스 안에 "인터페이스 - insert()호출 "이라는 로그를 찍는 메서드를 하나 만든다.

<bean>객체를 하나 만든다.

main에서 그 클래스-메서드를 호출한다.

=> 안되는것 확인.










'Today I learned' 카테고리의 다른 글

1월28일  (0) 2019.01.28
1월25일  (0) 2019.01.25
프런트 컨트롤러 디자인 패턴  (0) 2019.01.21
Ajax & Javascript 정리  (0) 2019.01.21
12월17일  (0) 2019.01.17

댓글