- 동일한 타입을 가진 bean 객체가 2개가 있을때 : 스프링을 초기화하는 과정에서 Exception을 발생시킴
→ @Autowired의 injection의 대상이 1개여야하는데 2개이상의 빈이 존재해 주입할 때 사용할 객체를 선택 불가
해결 : @Qualifier 사용
→ 사용할 의존객체를 선택할 수 있게 해줌
@Autowired적용한곳에 @Qualifier를 적용해주고 주입 할 id값을 (name="")로 적용시킴 - @Autowired 적용시 의존 객체 찾는 순서
동일 타입의 bean객체 검색 → 1개면 bean객체 사용
타입이 같은 bean객체가 두개 이상이면서 @Qualifier이 없는 경우 → 이름이 같은 빈 객체 검색
타입이 같은 bean객체가 두개 이상이면 @Qualifier로 지정한 bean객체를 찾음
→ 모두 해당되지 않으면 Exception 발생
암호화
알고리즘 종류
1. AES
- 블록 암호 알고리즘
- DES를 대체한 알고리즘으로 대칭키 알고리즘
- 블록크기 128비트
2. SHA
- HASH함수를 적용한 단방향 암호화 알고리즘으로 복호화 불가능
- MD5보다 느리지만 강화됨
암호화를 사용하기 위한 설정
Maven Repository: Search/Browse/Explore (mvnrepository.com)
spring security core검색
본인에 버전에 맞는 것을 복사해서 pom.xml(35번째줄) <dependency>로 넣기
spring security web 검색
본인에 버전에 맞는 것을 복사해서 붙여넣기한곳 밑에 다시 붙여넣기
spring security config검색
com.korea.crypto패키지에
CryptoUtil.java생성
package com.korea.crypto;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoUtil {
//MD5로 해시
public static String md5(String msg) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(msg.getBytes());
return CryptoUtil.byteToHexString(md.digest());
//Java에서 MD5, SHA를 이용한 알고리즘을 사용하려면 MessageDigest 클래스를 이용한다.
//MessageDigest 클래스에는 update() 메소드가 있는데, 이 메소드를 호출할 때마다 객체 내에 저장된 digest값이 계속해서 갱신 된다.
//최종적으로 digest() 메소드를 호출하면 그 값을 가져올 수 있다.
//Hash 함수를 통과 하기 전 원본 데이터를 메시지(message)라고 부르고, 통과된 이후 데이터를 다이제스트(digest)라고 부른다.
}
//SHA-256으로 해시
public static String sha256(String msg) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(msg.getBytes());
return CryptoUtil.byteToHexString(md.digest());
}
//바이트 배열을 HEX 문자열로 변환
public static String byteToHexString(byte[] data) {
StringBuilder sb = new StringBuilder();
for(byte b : data) {
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
//AES 256으로 암호화
public static String encryptAES256(String msg, String key) throws NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, InvalidParameterSpecException,
UnsupportedEncodingException, BadPaddingException,
IllegalBlockSizeException {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
byte[] saltBytes = bytes;
//Password-Based Key Derivation function 2
//지정된 암호 알고리즘의 SecretKeyFactory 오브젝트를 생성
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
//70000번 해시하여(돌려서) 256bit 길이의 키를 만든다.
//Iteration count => 해쉬의 횟수를 의미
//Iteration count가 1000이면 1000번을 해쉬한다는 의미
PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), saltBytes, 70000, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
/*
Salt : 패스워드를 해쉬하기 전에 랜덤값을 추가하여 키 스페이스를 늘린다.
salt는 암호문과 함께 저장된다. 복호화할 때는 암호화된 데이터로부터 salt를 분리해야 한다.
salt는 매번 다르게 생성되며 동일한 문장에 대해서도 다른 값이 생성된다.
*/
//알고리즘 / 모드 / 패딩
//CBC : Cipher Block chaining Mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
//Initial Vector(1단계 암호화 블록용), 복호화 : Decoding(암호화 하기전으로 되돌림) / 암호화 : Encoding
byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(msg.getBytes("UTF-8"));
byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
System.arraycopy(saltBytes, 0, buffer, 0, saltBytes.length);
System.arraycopy(ivBytes, 0, buffer, saltBytes.length, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, buffer, saltBytes.length + ivBytes.length, encryptedTextBytes.length);
return Base64.getEncoder().encodeToString(buffer);
}
public static String decryptAES256(String msg, String key) throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidKeySpecException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ByteBuffer buffer = ByteBuffer.wrap(Base64.getDecoder().decode(msg));
byte[] saltBytes = new byte[20];
buffer.get(saltBytes, 0, saltBytes.length);
byte[] ivBytes = new byte[cipher.getBlockSize()];
buffer.get(ivBytes, 0, ivBytes.length);
byte[] encryptedTextBytes = new byte[buffer.capacity() - saltBytes.length - ivBytes.length];
buffer.get(encryptedTextBytes);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), saltBytes, 70000, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
return new String(decryptedTextBytes);
}
}
com.korea.myapp.cryptomain패키지에
CryptoTest.java생성
package com.korea.myapp.cryptomain;
import com.korea.crypto.CryptoUtil;
public class CryptoTest {
public static void main(String[] args) throws Exception{
String plainText = "Hello, World";
String key ="secret key2";
//md5와 sha-256은 단방향 암호화로 비밀번호를 암호화하거나 데이터 전송등에서 무결성을 체크하는데 사용
//md5는 128bit로 서로 다른 값에 같은 해시가 발생하는 충돌이 확인되었고
//빠르게 해시가 가능하므로 비밀번호를 만드는데 안전하지 않음
//이제 sha-256 사용 권장
System.out.println("MD5 :"+plainText+" - "+CryptoUtil.md5(plainText));
System.out.println("SHA-256 :"+plainText+" - "+CryptoUtil.sha256(plainText));
//-AES 256은 키가 256bit 즉 32bit문자열 이어야함
//-salt를 사용하므로 동일한 값을 암호화 하더라도 암호된 값이 동일하지 않음
//-결과값에는 salt와 iv값을 추가하여 Base64로 엔코딩해 반환
String encrypted = CryptoUtil.encryptAES256("Hello, World", key);
System.out.println("AES-256 : enc - "+ encrypted);
System.out.println("AES-256 : dec - "+ CryptoUtil.decryptAES256(encrypted, key));
}
}
↓결과
MD5 :Hello, World - 82bb413746aee42f89dea2b59614f9ef
SHA-256 :Hello, World - 03675ac53ff9cd1535ccc7dfcdfa2c458c5218371f418dc136f2d19ac1fbe8a5
AES-256 : enc - Yi97wxwk+Fj6g0VVQc3MUMq56ZZxX3+dsFk5nmj+OTsJT8mwcBA0Cwgy+Y9c71yBPd72sw==
AES-256 : dec - Hello, World
src/main/java패키지에
LoginController.java
package com.korea.myapp;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/login.do") //요청 경로
public class LoginController {
public LoginController() {
System.out.println("LoginController 빈 생성 Lee");
}
@Autowired
BCryptPasswordEncoder pwEncoder;
//salting과 Key Stretching을 구현한 해시 함수 중 가장 널리 사용 되는 것이 bcrypt
//bcrpt는 처음부터 비밀번호를 단방향 암호화하기 위해 만들어진 해시 함수
@RequestMapping(method = RequestMethod.GET)
public String form() {
return "/login/loginForm"; // login/loginForm.jsp(뷰 페이지 이름)
}
@RequestMapping(method = RequestMethod.POST)
public ModelAndView loginproc(LoginDTO dto, HttpSession session){
//ModelAndView : 컨트롤러 처리 결과 후 응답할 view와 view에 전달할 값을 저장 및 전달하는 클래스
String id = dto.getId();
String passwd = dto.getPasswd();
String tel = dto.getTel();
System.out.println("id : "+id);
System.out.println("passwd : " + passwd);
System.out.println("tel : " + tel);
System.out.println("---------------------------------");
System.out.println("id : "+id);
System.out.println("passwd : " + pwEncoder.encode(passwd));
System.out.println("tel : " + pwEncoder.encode(tel));
//BcrptPasswordEncoder는 복호화가 되지 않으므로
//로그인할때도 암화한 것을 비교해 로그인 하면 됨
ModelAndView mav = new ModelAndView();
if(id.equals("abc") && (passwd.equals("1234")) && (tel.equals("000")) ) {
//request영역
mav.addObject("r_id",id);
mav.addObject("r_passwd",passwd);
mav.addObject("r_tel",tel);
//passwd를 encode로 암호화
String encode_1 = pwEncoder.encode(passwd);
String encode_2 = pwEncoder.encode(tel);
mav.addObject("r_passwd2",encode_1);
mav.addObject("r_tel2",encode_2);
//session영역
session.setAttribute("s_id", id);
session.setAttribute("s_passwd", passwd);
session.setAttribute("s_passwd2", encode_1);
session.setAttribute("s_tel", tel);
session.setAttribute("s_tel2", encode_2);
mav.addObject("msg","로그인 성공");
mav.setViewName("/login/loginProc");
}else {
mav.addObject("msg","<p>아이디와 패스워드가 일치하지 않습니다.</p>");
mav.addObject("link","<a href='javascript:history.go(-1)'>다시시도</a>");
mav.setViewName("/login/msgView");
//msgView.jsp
}
return mav;
}
}
BCryptPasswordEncoder
- Spring Security 프레임워크에서 제공하는 클래스 → 비밀번호를 암호화(해시)하는데 사용
- 단순히 1회 해시시키는것이 아니라 salt를 부여해 여러면 해싱하므로 안전하게 암호 관리 가능
- BCrypt는 같은 비밀번호를 암호화하더라도 해시 값은 매번 다른 값이 도출됨
→ 사용자가 제출된 비밀번호와 암호화되어 저장된 비밀번호의 일치 여부를 확인하는 메서드 제공
LoginDTO.java생성
package com.korea.myapp;
public class LoginDTO {
private String id;
private String passwd;
private String loginType;
private String tel;
public LoginDTO() {
super();
}
public LoginDTO(String id, String passwd, String loginType) {
super();
this.id = id;
this.passwd = passwd;
this.loginType = loginType;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
@Override
public String toString() {
return "LoginDTO [id=" + id + ", passwd=" + passwd + ", loginType=" + loginType + ", tel=" + tel + "]";
}
}
src/main/webapp/spring
spring_security.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:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.1.xsd">
<bean id="bcryptPasswordEncoder"
class ="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
web.xml
<context-param>의 <param-value>에 /WEB-INF/spring/sping_security.xml 추가
한글을 사용하기 위해 filter추가
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml
/WEB-INF/spring/spring_security.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- utf-8 encoding filter한글처리를 위한 필터 -->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
view폴더에서
loginForm.jsp생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원로그인</title>
</head>
<body>
</body>
<div>로그인</div>
<form action ="/login.do" method="post" name="login">
<table>
<tr>
<th>아이디</th>
<td><input type="text" name="id" value="" size="20" ></td>
</tr>
<tr>
<th>비밀번호</th>
<td><input type="password" name="passwd" value="" size="20"></td>
</tr>
<tr>
<th>전화번호</th>
<td><input type="text" name="tel" value="" size="20" ></td>
</tr>
</table>
<div>
<input type="submit" value="로그인">
</div>
</form>
</html>
loginProc.jsp생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>longin/loginProc.jsp 로그인 성공</title>
</head>
<body>
1) jsp사용 결과<br>
<%=request.getAttribute("msg") %><br>
아이디 : <%=request.getAttribute("r_id") %><br>
비밀번호 : <%=request.getAttribute("r_passwd") %><br>
비밀번호2 : <%=request.getAttribute("r_passwd2") %><br>
전화번호 : <%=request.getAttribute("r_tel") %><br>
전화번호 : <%=request.getAttribute("r_tel2") %><br>
세션아이디 : <%=session.getAttribute("s_id") %><br>
세션 비밀번호 : <%=session.getAttribute("s_passwd") %><br>
세션 비밀번호2 : <%=session.getAttribute("s_passwd2") %><br>
세션 전화번호 : <%=session.getAttribute("s_tel") %><br>
세션 전화번호2 : <%=session.getAttribute("s_tel2") %><br>
<hr>
<hr size="5" color="#cccccc">
2) EL 사용 결과<br>
${msg }<br>
아이디 : ${r_id }<br>
비밀번호 : ${r_passwd }<br>
비밀번호2 : ${r_passwd2 }<br>
전화번호 : ${r_tel }<br>
세션 아이디 : ${s_id }<br>
세션 비밀번호 : ${s_passwd }<br>
세션 비밀번호2 : ${s_passwd2 }<br>
세션 전화번호 : ${s_tel }<br>
세션 전화번호2 : ${s_tel2 }<br>
</body>
</html>
msgView.jsp생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login/msgView 로그인 실패</title>
</head>
<body>
<br><br><br>
<h1>
<%
//Script를 이용한 경우
//String msg = (String)request.getAttribute("msg");
//String link = (String)request.getAttribute("link");
//out.println(msg);
//out.println("<br><br>");
//out.pringln(link);
%>
${msg }<br><br>
${link }<br>
${link }<br>
</h1>
</body>
</html>
↓결과
loginProc.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>longin/loginProc.jsp 로그인 성공</title>
</head>
<body>
1) jsp사용 결과<br>
<%=request.getAttribute("msg") %><br>
아이디 : <%=request.getAttribute("r_id") %><br>
비밀번호 : <%=request.getAttribute("r_passwd") %><br>
비밀번호2 : <%=request.getAttribute("r_passwd2") %><br>
전화번호 : <%=request.getAttribute("r_tel") %><br>
전화번호 : <%=request.getAttribute("r_tel2") %><br>
세션아이디 : <%=session.getAttribute("s_id") %><br>
세션 비밀번호 : <%=session.getAttribute("s_passwd") %><br>
세션 비밀번호2 : <%=session.getAttribute("s_passwd2") %><br>
세션 전화번호 : <%=session.getAttribute("s_tel") %><br>
세션 전화번호2 : <%=session.getAttribute("s_tel2") %><br>
<hr>
<hr size="5" color="#cccccc">
2) EL 사용 결과<br>
${msg }<br>
아이디 : ${r_id }<br>
비밀번호 : ${r_passwd }<br>
비밀번호2 : ${r_passwd2 }<br>
전화번호 : ${r_tel }<br>
세션 아이디 : ${s_id }<br>
세션 비밀번호 : ${s_passwd }<br>
세션 비밀번호2 : ${s_passwd2 }<br>
세션 전화번호 : ${s_tel }<br>
세션 전화번호2 : ${s_tel2 }<br>
</body>
</html>
msgView.jsp생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login/msgView 로그인 실패</title>
</head>
<body>
<br><br><br>
<h1>
<%
//Script를 이용한 경우
//String msg = (String)request.getAttribute("msg");
//String link = (String)request.getAttribute("link");
//out.println(msg);
//out.println("<br><br>");
//out.pringln(link);
%>
${msg }<br><br>
${link }<br>
${link }<br>
</h1>
</body>
</html>
↓결과
AOP(Aspect Oriented Programming)
- 관점 지향 프로그래밍
- 핵심적인 관점, 부가적인 관점으로 나누고 그 관점을 기준으로 모듈화
용어 | 의미 |
Aspect | 흩어진 관심사를 모듈화 한것 |
Target | Aspect를 적용하는 곳(클래스, 메서드 등) |
Advice | 실질적인 어떤일을 해야할 지에 대한것 실질적인 부가기능을 담은 구현체 |
Join Point | Advice가 적용될 위치 혹은 끼어들 수 있는 시점 |
Point Cut | JoinPoint의 상세한 스펙을 정의한 것 구체적으로 Advice가 실행될 시점을 정함 |
- AOP를 적용하는 방법
1. 컴파일 타임 적용
2. 로드 타임 적용
3. 런타임 적용방법(스프링 AOP가 주로 사용하는 방법)
: Proxy Bean을 만들어 Proxy Bean이 Aspect코드를 추가해 동작하는 방법
src/main/java
aop_1패키지에
Boy.java생성
package aop_1;
public class Boy {
public void runSomething() {
System.out.println("Boy.java");
try {
System.out.println("컴퓨터");
}catch(Exception e) {
if(e.getMessage().equals("불")) {
System.out.println("119");
}
}finally {
System.out.println("잠");
}
System.out.println("집을 나섬");
}
}
Girl.java생성
package aop_1;
public class Girl {
public void runSomething() {
System.out.println("Girl.java");
try {
System.out.println("요리");
}catch(Exception e) {
if(e.getMessage().equals("불")) {
System.out.println("119");
}
}finally {
System.out.println("잠");
}
System.out.println("집을 나섬");
}
}
Start.java생성
package aop_1;
public class Start {
public static void main(String[] args) {
Boy romeo = new Boy();
Girl juliet = new Girl();
romeo.runSomething();
System.out.println();
juliet.runSomething();
}
}
↓결과
Boy.java
컴퓨터
잠
집을 나섬
Girl.java
요리
잠
집을 나섬
aop_2패키지 생성
Person.java 인터페이스 생성
package aop_2;
public interface Person {
void runSomething();
}
Boy.java생성
package aop_2;
public class Boy implements Person{
@Override
public void runSomething() {
System.out.println("컴퓨터");
}
}
Girl.java생성
package aop_2;
public class Girl implements Person {
@Override
public void runSomething() {
System.out.println("요리");
}
}
MyAspect.java생성
package aop_2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
//runSomething()이 실행되기 전에
@Before("execution(* runSomething())")
public void before(JoinPoint joinpoint) {
System.out.println("얼굴 인식 확인 : open");
}
}
aop_2.xml(spring Bean configuration file)
<?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">
<!-- @AspectJ를 사용하기 위해 다음 코드를 Spring 설정에 추가
@Aspect가 적용된 빈을 Aspect로 사용할 수 있게 한다 -->
<aop:aspectj-autoproxy/>
<bean id="myAspect" class="aop_2.MyAspect"></bean>
<bean id="boy" class="aop_2.Boy"></bean>
<bean id="girl" class="aop_2.Girl"></bean>
</beans>
Start.java생성
package aop_2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Start {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop_2/aop_2.xml");
Person romeo = context.getBean("boy", Person.class);
Person juliet = context.getBean("girl", Person.class);
romeo.runSomething();
System.out.println();
juliet.runSomething();
}
}
pom.xml에 AspectJ뒤 추가
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
↓결과
얼굴 인식 확인 : open
컴퓨터
얼굴 인식 확인 : open
요리
'Spring' 카테고리의 다른 글
[Spring]Lambda 표현식 (0) | 2023.06.05 |
---|---|
221128_Spring_AOP2 (0) | 2022.11.28 |
221124_Spring_국비_DI/DesignPattern/@Resource/@Autowired/@Inject (0) | 2022.11.24 |