[Spring] 어노테이션

김장환·2022년 8월 23일

Spring

목록 보기
7/17

어노테이션 개념

어노테이션은 환경설정에 관한 xml에 사용되며 자바코딩의 간결화를 시켜주어 코드대신 사용이 가능하다.

어노테이션의 장점

  • 시스템 복잡성이 아니라면 어노테이션 사용은 적합하게 쓰이면 코드가 간결해지고 유지보수가 용이
  • 대형 시스템엔 계층 구조가 잘 파악되기 위해서는 xml 사용이 필수
    (어노테이션과 연관있는 클래스를 빈즈로 등록->xml)

어노테이션의 단점

어노테이션과 관련된 클래스와 같이 연동->이해하기가 쉽지않다.

  • 어노테이션 은 메타정보가 소스코드에 들어가므로 파악하기 어려움
    (재 컴파일이 필요하다.)

  • 소스코드가 같이 제공되지 않으면 사용에 제약이 따름
    (어노테이션 과 클래스 같이 사용할것)


@Required

@Required를 적용한 메서드를 반드시 호출해야되는데 만약에 호출하지 않으면 에러를 유발시킨다.

예제를 통해 정리하고자 한다.

1. Camera.java 생성
-@Required적용

package anno1;

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

public class Camera extends Object{// 모든객체는 extends Object를 생략하고있다

	private int number;//카메라수 0

	@Required
	public void setNumber(int number) {
		this.number = number;
		System.out.println("setNumber() call");
	}
	
	//메서드이름 위에 기술=>메서드명이 오버라이딩이 된 메서드인지 확인해주는 기능을 가진 어노테이션
	@Override
		public String toString() {
			// TODO Auto-generated method stub
			return "Camera[number="+number+"]";
		}
}


app2.xml 내용추가

  • @Required를 사용하기 위한 빈즈클래스를 등록한다.
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />

<bean id="camera" class="anno1.Camera">
	<property name="number" value="30" /> 
</bean>

만약 property를 주석처리하거나 지우면 @Required로인해 number값은 null이 아니라 에러가 뜬다.


3. Main.java
-이전꺼에서 id값과 클래스명만 수정해준다.

package anno1;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//1.메모리관리->xml파일이 여러개->배열로 관리->파일명부여  // xml이 여러개면 ,로 쭉쭉 나열 가능
			String [] configLocation=new String[] {"app2.xml"};	
		//2.xml파일을 메모리에 올려줄 수 있는 클래스를 통해서 객체를 생성
			AbstractApplicationContext context=
					new ClassPathXmlApplicationContext(configLocation);
		//3.자바프로그램이 종료->자동적으로 context객체도 같이 종료 설정	
			context.registerShutdownHook();
		//4.xml에서 만들어진 객체를 가져와서 처리	
		
			//형식) ~getBean("의존성객체를 얻어올id",형변환을 할 클래스명.class)
			   Camera camera=
		               context.getBean("camera",Camera.class);
		         System.out.println("camera=>"+camera);//10
			
			
		//5.context도 종료
			context.close();//메모리에 올려놓은 모든 빈즈객체->메모리해제
	}

}


@Autowired

@Autowired가 붙으면 해당 변수 또는 메서드의 타입을 체크한후 그 타입의 객체가 메모리에 존재하는지를 확인한 후레 그 객체를 변수에 주입한다.

  • 생성자나 메소드, 멤버변수 위에 모두 사용할수 있다.
    =>주로 멤버변수 위에 선언한다

  • 차이점
    메서드에 적용 =>자동으로 객체를 넣어줌
    멤버변수에 적용 =>Setter Method가 필요없다.(코딩양이 줄어들어 개발시간이 단축)

예제를 통해 정리하고자 한다.


  1. SmsSender.java생성
package anno2;

//문자전송 시스템
public class SmsSender {}


  1. SystemMoniter.java
  • @Autowired 적용
  • @Required도 적용 (어노테이션은 겹쳐서 사용가능)
  • 메서드에 적용

package anno2;

import javax.inject.Inject;



//문자를 전송=>기간을 정하기
public class SystemMoniter {

	private long periodTime;//기간
	//has a 관계 =>두 클래스가 연결
	//@Autowired 

	private SmsSender sender;
	//Setter Method
	public void setPeriodTime(long periodTime) {
		this.periodTime = periodTime;
		System.out.println("setPeriodTime() call");
	}
	
	@Required
	@Autowired
	public void setSender(SmsSender sender) {
		this.sender = sender;
		System.out.println("setSender() call=>"+sender);
	}
	
	@Override
	public String toString() {// Setter메서드로 잘 설정됐는지 확인할수있음
		// TODO Auto-generated method stub
		return "SystemMoniter[periodTime="+periodTime+",sender="+sender+"]";
	}
}


3. app2.xml 내용추가

  • @Autowired를 사용하기위한 빈즈클래스 등록
  • @Autowired로 인해 property태그 또는 p네임스페이스를 생략해도 적용된다
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
	
	<!-- @Autowired로 인해 property태그 또는 p네임스페이스를 생략해도 적용된다 -->
<bean id="moniter" class="anno2.SystemMoniter" />
<bean id="sender" class="anno2.SmsSender" />

4. Main 생성

  • 예전에 했던 Moniter파일에서 변함이 없기에 그대로 복사해와서 실행한다
package anno2;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//1.메모리관리->xml파일이 여러개->배열로 관리->파일명부여  // xml이 여러개면 ,로 쭉쭉 나열 가능
			String [] configLocation=new String[] {"app2.xml"};	
		//2.xml파일을 메모리에 올려줄 수 있는 클래스를 통해서 객체를 생성
			AbstractApplicationContext context=
					new ClassPathXmlApplicationContext(configLocation);
		//3.자바프로그램이 종료->자동적으로 context객체도 같이 종료 설정	
			context.registerShutdownHook();
		//4.xml에서 만들어진 객체를 가져와서 처리	
		
			//형식) ~getBean("의존성객체를 얻어올id",형변환을 할 클래스명.class)
			SystemMoniter moniter=
					context.getBean("moniter",SystemMoniter.class);
			System.out.println("moniter=>"+moniter);
			
			
		//5.context도 종료
			context.close();//메모리에 올려놓은 모든 빈즈객체->메모리해제
	}

}


5. @Autowired을 메서드위가 아니라 멤버변수에 적용한다면?

  • SystemMoniter.java에서 Setter Method는 필요 없어지기에 지워주고 멤버변수 위에 @Autowired을 적용한다.
    => 이렇게 해도 객체를 얻어올수있어 결과는 같다

  • 멤버변수에 적용하면 굳이 setter 메서드를 안만들어도되서 더 편리하다 그래서 주로 이렇게 변수위에 만든다.

package anno2;

//문자를 전송=>기간을 정하기
public class SystemMoniter {

	private long periodTime;//기간
	//has a 관계 =>두 클래스가 연결
	//@Autowired(required=false) //빈즈객체가 없어도 에러유발 X 있으면 넣어준다
	@Autowired
	private SmsSender sender;
	//Setter Method
	public void setPeriodTime(long periodTime) {
		this.periodTime = periodTime;
		System.out.println("setPeriodTime() call");
	}
	
	@Override
	public String toString() {// Setter메서드로 잘 설정됐는지 확인할수있음
		// TODO Auto-generated method stub
		return "SystemMoniter[periodTime="+periodTime+",sender="+sender+"]";
	}
}


6. @Autowired(required=false)옵션 추가 =>빈즈객체가 없어도 에러유발 X 있으면 넣어준다


@Inject

@Inject는 @Autowired와 비슷한 기능으로 더 최신버전이다.
따라서 위에 @Autowired의 예제에서 @Autowired를 @Inject로만 바꾼다.

  • @Autowired 와 @Inject의 차이점
    1) @Autowired =>xml 파일에 빈즈클래스 등록 (O)
    2) @Inject =>xml 파일에 빈즈클래스 등록 (X)

@inject는 외부패키지라 pom.xml에 추가해줘야한다

		<!--  @Inject 추가 -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>


@Resource

@Resource 객체의 이름을 이용하여 의존성 주입을 처리한다
name속성을 사용할수있어서 해당 이름으로 생성된 객체를 검색하여 의존성 주입을 처리한다.

@Resource 을 사용하기위해서 pom.xml에 소스를 넣어야한다.

@Resource와 @Autowired 의 공통점과 차이점

공통점:자동으로 객체를 찾아서 넣어준다.
차이점: byName vs byType
@Resource - byName(이름으로 찾아서 자동으로 객체를 넣어주는 개념)
같은 클래스 자료형이 여러개있을때 어떻게 구분?-> 이름으로 구분
@Autowired - byType(같은 자료형을 찾아서 자동으로 객체를 넣어주는개념)



@Resource 을 사용하기위해서 pom.xml에 소스를 넣어야한다.


<!--  @Resource -->
		<dependency>
		    <groupId>javax.annotation</groupId>
		    <artifactId>javax.annotation-api</artifactId>
		    <version>1.3.2</version>
         </dependency>

1. Camera.java 생성

package anno3;

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

public class Camera extends Object{// 모든객체는 extends Object를 생략하고있다

	private int number;//카메라수 0

	@Required
	public void setNumber(int number) {
		this.number = number;
		System.out.println("setNumber() call");
	}
	
	//메서드이름 위에 기술=>메서드명이 오버라이딩이 된 메서드인지 확인해주는 기능을 가진 어노테이션
	@Override
		public String toString() {
			// TODO Auto-generated method stub
			return "Camera[number="+number+"]";
		}
}


  1. HomeController.java 생성
  • 멤버변수 3개 중 1개는 멤버변수에 @Resource 적용
    나머지 2개는 Setter Method에 @Resource 적용
  • 형식) @Resource(name="빈즈의 구분자id")


package anno3;

import javax.annotation.Resource;


public class HomeController {
	
	@Resource(name="camera2")//=> <bean id="camera2" class~  
	private Camera camera2;	// 멤버변수에 @Resource 적용
	
	private Camera camera3;
	private Camera camera4;
		
	//   Setter Method에 @Resource 적용
	@Resource(name="camera3")  //id가 camera3인 클래스객체를찾아서 저장
	public void setCamera3(Camera camera3) { 
		this.camera3 = camera3;
		System.out.println("setCamera3() call");
	}
	@Resource(name="camera4") //id가 camera4인 클래스객체를찾아서 저장
	public void setCamera4(Camera camera4) {
		this.camera4 = camera4;
		System.out.println("setCamera4() call");
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "HomeController[camera2="+camera2+",camera3="+camera3+",camera4="+camera4+"]";
	}
	
}

멤버변수에 @Resource을 적용하면 Setter 메서드는 지워도된다.

3. app2.xml 내용추가

  • @Resource를 사용하기위한 빈즈클래스 등록
  • p네임스페이스 적용

<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />

<bean id="camera2" class="anno3.Camera" p:number="2" />
<bean id="camera3" class="anno3.Camera" p:number="3" />
<bean id="camera4" class="anno3.Camera" p:number="4" />
	
	<!-- has a 관계 -->
<bean id="homeController" class="anno3.HomeController" />


4. Main 생성

  • 이전꺼에서 id값과 클래스명만 수정해준다.
package anno3;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//1.메모리관리->xml파일이 여러개->배열로 관리->파일명부여  // xml이 여러개면 ,로 쭉쭉 나열 가능
			String [] configLocation=new String[] {"app2.xml"};	
		//2.xml파일을 메모리에 올려줄 수 있는 클래스를 통해서 객체를 생성
			AbstractApplicationContext context=
					new ClassPathXmlApplicationContext(configLocation);
		//3.자바프로그램이 종료->자동적으로 context객체도 같이 종료 설정	
			context.registerShutdownHook();
		//4.xml에서 만들어진 객체를 가져와서 처리	
		
			//형식) ~getBean("의존성객체를 얻어올id",형변환을 할 클래스명.class)
			HomeController home=
		               context.getBean("homeController",HomeController.class);
		         System.out.println("home=>"+home);
			
			
		//5.context도 종료
			context.close();//메모리에 올려놓은 모든 빈즈객체->메모리해제
	}

}


@PostConstruct, @PreDestroy

@PostConstruct와 @PreDestroy는 간단히 개념설명만 하고자 한다.

  • @PostConstruct=빈즈객체 생성전에 초기화작업을 하고자할때(=생성자)사용
    =>물건사기전에 돈을 선불로 지불하는것 가트이 먼저 초기화 작업

  • @PreDestroy=객체가 생성된 후에 작업->메모리 작업
    =>물건을 먼저받고 나중에 후불로 돈지불하는것 처럼 나중에 작업


@Component

Camera 메서드를 빈즈를 생성하려면
< bean id="camera" class="anno5.Camera" /> 이런식으로 수동으로 작성한다
하지만 만약 이런게 30개가있다면? 수동으로 일일이 하긴 힘들다.

따라서 이럴때 자동으로 만들어줄때 사용하는 어노테이션이 @Component이다.
그동안 배운 어노테이션들을 일일이 설정하지 않고 @Component 하나로 적용할수있다

@Component
클래스와 관련된 어노테이션으로 스프링컨테이너가 어느 특정패키지를 지정 하면 자동적으로 그 패키지에 들어가 있는 모든 클래스중에서 @Component가 붙어있는 클래스를 빈즈로 자동으로 등록한다.


1. 각각 다른클래스에 Camera.java, Camera2.java, Camera3.java를 생성

Camera.java

package anno5;

import org.springframework.stereotype.Component;

/*
 *빈즈에 관련된 클래스가 30개 이상이라면
 *
 * <bean id="camera" class="anno5.Camera" /> =>수동으로 등록   /밑에 Camera를 빈즈로 생성 근데 이런게 30개가있다면?  수동으로 일일이 하긴 힘듬
 *   ,,,,
 *   
 *   @Component =>클래스와 관련된 어노테이션
 *   스프링컨테이너가 어느 특정패키지를 지정 ->자동적으로 그 패키지에 들어가 있는 
 *   모든 클래스중에서 @Component가 붙어있는 클래스를 빈즈로 등록(자동) 
 */
@Component
public class Camera{} //기본생성자 자동생성->호출

Camera2.java


package anno5;

import org.springframework.stereotype.Component;


@Component
public class Camera2{} //기본생성자 자동생성->호출

Camera3.java


package anno5;

import org.springframework.stereotype.Component;


@Component
public class Camera3{} //기본생성자 자동생성->호출


2. HomeController2 생성

  • @Component 적용

package anno5;

import javax.inject.Inject;

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

@Component
public class HomeController2 {
	
	@Inject
	private Camera camera;	
	
	@Autowired
	private Camera camera2;
	
	@Autowired
	private Camera camera3;
	 
	// 위에 3개에는 어노테이션을 굳이 안해도됨 (@Component 을 적용했뗴문에) 
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "HomeController2[camera="+camera+",camera2="+camera2+",camera3="+camera3+"]";
	}
}

클래스 위에 @Component 을 적용했기에 @Inject, @Autowired, @Autowired 이 세개는 굳이 안해도된다.


3. appScan.xml 생성

  • < context:component-scan base-package=""/>을 사용
    =>클래스들을 스캔하여 @Component가 설정된 클래스들을 자동으로 객체생성한다
  • base-package으로 적용범위를 지정할수있다

어노테이션 및 특정패키지에 들어가 있는 모든 클래스의 빈즈객체를 빈즈로 자동으로 등록(옵션)

이전에는 이렇게 수동으로 직접 4개를 등록했었다.

<bean id="homeController2" class="anno5.HomeController2" />
<bean id="camera"  class="anno5.Camera" />
<bean id="camera2"  class="anno5.Camera2" />	
<bean id="camera3"  class="anno5.Camera3" />	

하지만 @Component을 적용하면 다음과 같이 간단히 가능하다.

<!--  @Component로 등록한 모든 객체를 자동으로 다 생성-->
<context:component-scan base-package="anno5" />

4. Main.java 생성

  • appScan.xml을 새로만들었기에 appScan.xml로 수정한다
  • id나 name을 xml에서 정하지 않았는데 되는 이유 => 자동으로 기본클래스이름 통해서 가져옴 (클래스이름에서 앞글자 소문자)
  • 임의로 정해주고싶다면 @Conmponent("부여할 id이름지정")
    =>HomeController2.java에서 @Component("home")을 지정하여 id를 home으로 설정

package anno5;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//1.메모리관리->xml파일이 여러개->배열로 관리->파일명부여  // xml이 여러개면 ,로 쭉쭉 나열 가능
			String [] configLocation=new String[] {"appScan.xml"};	
		//2.xml파일을 메모리에 올려줄 수 있는 클래스를 통해서 객체를 생성
			AbstractApplicationContext context=
					new ClassPathXmlApplicationContext(configLocation);
		//3.자바프로그램이 종료->자동적으로 context객체도 같이 종료 설정	
			context.registerShutdownHook();
		//4.xml에서 만들어진 객체를 가져와서 처리	
		
			//형식) ~getBean("의존성객체를 얻어올id",형변환을 할 클래스명.class)
			HomeController2 home2=
		               context.getBean("home",HomeController2.class);
		         System.out.println("home2=>"+home2);
		       //  id나 name을 xml에서 정하지 않았는데 되는 이유 => 자동으로 기본클래스이름 통해서 가져옴 (클래스이름에서 앞글자 소문자)
		       //   임의로 정해주고싶다면 @Conmponent("부여할 id이름지정")
		//5.context도 종료
			context.close();//메모리에 올려놓은 모든 빈즈객체->메모리해제
	}

}

@Component을 사용하면 간단히 가능하지만 굳이 여러 어노테이션들을 하나하나 배운 이유는 기초를 알아야 재대로 이해할수있기 때문이다.


@Transactional

sql구문들을 트랜잭션처리해주는 어노테이션


2022-08-23

0개의 댓글