๐ฅ [Spring] AOP (+annotation) & Spring-Boot
AOP
JoinPoint
์ Adivce
์ Cross Cutting Concern์ด Target
ํด๋์ค์ ์ด๋ ๋ฉ์๋, ์ด๋ ์์น์ ๊ฝํ๋ ๊ณณ์ด๋ค.
JoinPoint
์คํ๋ฅผ ์์ธํ๊ฒ ์ ์ด๋์ ๊ฒ์ PointCut
์ด๋ค.
์ ๋ฒ ํฌ์คํธ์ AOP advice class ๋ฅผ ์ฐธ๊ณ ํ์
1
2
3
4
5
6
7
8
9
10
public class LoggingAdvice {
private Log log = LogFactory.getLog(getClass());
public void logPush(ProceedingJoinPoint pjp) throws Throwable{
log.info("\ncheck...before logging...");
Object ret=pjp.proceed();//target์ผ๋ก weaving๋๋ ์์
System.out.println("target method return..."+ret);
}
}
ProceedingJoinPoint
๊ฐ proceed()
ํจ์๋ฅผ ๊ฐ์ง๊ณ ์๋๋ฐ, ์ด๊ฒ์ด Target์ด ํธ์ถ๋๋ฉด proceed()
๊ฐ
ํธ์ถ๋์ด Targetํธ์ถ ์์น๋ฅผ ๊ฐ์งํ๋ค.
๋ฐ๋ผ์ ๊ทธ๊ณณ์ ์ํ๋ ๋ถ๋ถ์ Advice
๋ฅผ weaving
ํ ์ ์๋ ๊ฒ์ด๋ค.
Object ret=pjp.proceed()
Target ํจ์์ return ํ์ ์ผ๋ก ๋ฐํ๋๋ค. ๋ฐ๋ผ์ ret๋ Targetํจ์์ returnํ์ ์ด ๋ค์ด๊ฐ๋ค.
1
2
3
4
5
6
7
8
<!-- Advice์ ์ด๋ค ๋ฉ์๋๊ฐ Target์ ์ด๋๋ก weaving ๋๋์ง๋ฅผ ์ง์ ํด์ค์ผ ํ๋ค. => <aop:config> -->
<!--Advice์ ์ด๋ค ๊ธฐ๋ฅ์ด Target ๋ฉ์๋ ํธ์ถ ๋ ๋ weaving ๋๋์ง๋ฅผ ์์ธํ ์ค์ ํ๋ ๋ถ๋ถ์ด๋ค. -->
<aop:config>
<aop:aspect id="loggingAspect" ref="logging">
<aop:pointcut expression="execution(* spring.aop..*(..))" id="pc"/> <!--Target์ ํ์ฅ์๋ ์๊ด์๊ณ , spring.aop ๋ฐ์ ์๋ฌด๊ฑฐ๋ ์๊ด์์ด -->
<aop:around method="logPush" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
<aop:pointcut>
์ Target
์ ์ด๋ ๋ถ๋ถ์ด JoinPoint
์ธ์ง ์ ํด์ฃผ๊ณ ์๋ค.
(..) ์์ ์๋ ๊ฒ์ ํจ์ ๋ช ์ด๋ค.
*(..) : Target ํด๋์ค์ ๋ชจ๋ ํจ์์ ์ ์ฌํ๋ค.
spring.aop.Service : spring โ aop โ Serviceํด๋์ค ์ ์๋ ๋ชจ๋ ํจ์
spring.aop. : spring โ aop ์ ์กด์ฌํ๋ ๋ชจ๋ ํด๋์ค
์ด๋ ๊ฒ xml
์ ์์ฑํ๋ฉด ๊ธธ์ด์ง ์ ์๋ค.
๋ฐ๋ผ์ Annotation
์ผ๋ก ๋ง๋ค์ด์ผ ํ๋ค.
์ ๋ฒ์ ๋ง๋ค์๋ LogginAdvice(Advice Class) ๋ฅผ Annotation
์ผ๋ก ๋ณํํ์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package spring.aop.anno;
/*
* ๋ถ์์ ์ธ ๊ด์ฌ์ฌ๋ง ๋ชจ์ ๋์ ํด๋์ค...Cross Cutting Concern
* Advice Class
* Annotation์ผ๋ก ๋ง๋ ๋ค๋ ์๋ฏธ๋ aop์ค์ ์ ์ฝ๋๋ก ์์ฑํ๋ค๋ ์๋ฏธ
*/
//<aop:config> ์ค์ ๋ถ๋ถ์ ์ด์ xml์์ ์ฌ๋ผ์ง๋ค.
@Aspect
public class LoggingAdvice {
private Log log = LogFactory.getLog(getClass());
//Target ํด๋์ค ๋ฉ์๋์ return ํ์
์ด String์ด๊ณ springํจํค์ง ์๋์ ์๋ ๋ชจ๋ ํ์ ํจํค์ง ์ค์์
//ํด๋์ค ์ด๋ฆ์ด Product๋ก ์์ํ๋ ํด๋์ค ...ํจ์๋ช
์ด delete๋ก ์์ํ๊ณ ...์ธ์๊ฐ์ด 1๊ฐ ์ด์์ธ
@Around("execution(String spring..Product*.delete*(..))")
public void logPush(ProceedingJoinPoint pjp) throws Throwable{
log.info("\ncheck...before logging...");
Object ret=pjp.proceed();//target์ผ๋ก ์๋น๋๋ ์์
System.out.println("target method return..."+ret);
}
}
์์ ์ฝ๋์์ ์ฃผ์์ ์ ์ดํด๋ณด์.
@Aspect
์ ์ฒ์์ ์ ์ธํด์ฃผ๊ณ , @Around
๋ฅผ Cross Cutting Concern
์์ ๋ฃ์ด์ค๋ค.
์์ Advice
๋ Annotation
์ผ๋ก ๋ง๋ค์ด์ Bean
์ค์ ๋ฌธ์๋ฅผ ๋ค์ ์ค์ ํด์ผ ํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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.0.xsd">
<!-- Target 2๊ฐ (member, product), Advice 1๊ฐ (logging) -->
<bean id="member" class="spring.aop.anno.MemberService"/>
<bean id="product" class="spring.aop.anno.ProductService"/>
<bean id="logging" class="spring.aop.anno.LoggingAdvice"/>
<!--aopconfig์ annotation์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ์๋ ค์ค์ผ ํ๋ค. -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> //์ถ๊ฐ!!!!!!!!!!!!!!!!!
</beans>
์ด์ ์ xml
์์ aop
๊ฐ ๋ค ์ฌ๋ผ์ง๊ณ ์์์ ์๋กญ๊ฒ ์ถ๊ฐํ ํ์ค๋ง ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
๊ฒ์์ด ์์ ๊ธฐ๋ฅ ๊ตฌํ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2 align="center">Product Register Form...</h2>
<form action="myProduct.do" method="post">
์ํ๋ช
: <input type="text" name="name"><br><br>
์ ์กฐ์ฌ : <input type="text" name="maker"><br><br>
๊ฐ ๊ฒฉ : <input type="text" name="price"><br><br>
<input type="submit" value="์ํ๋ฑ๋ก">
</form>
+++++++++++++++++++++++++++++++++++++++++++++++++++
<br><br>
<form action="productSearch.do">
์ํ๋ช
๊ฒ์ <input type="text" name="word"><br><br>
<input type="submit" value="๊ฒ์ํ๊ธฐ">
</form>
<p>
<hr>
<a href="report.do">๊ฒ์์ด ์์๋ณด๊ธฐ</a>
</body>
</html>
๋จผ์ ์ํ๋ช ๊ฒ์์ ์ํ๋ช ์ ์ ๋ ฅํ๊ณ ๊ฒ์ํ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด โproductSearch.doโ๋ก ์์ฒญ์ด ๋ค์ด๊ฐ๋ค.
1
2
3
4
5
6
7
@RequestMapping("productSearch.do")
public String findByProductName(String word, Model model)throws Exception{
System.out.println("ProductController...findByProductName() :: "+word);
model.addAttribute("list", myProductService.findByProductName(word));
return "find_result";
}
๊ทธ๋ฆฌ๊ณ find_result
๋ก ๋ค๋น๊ฒ์ด์
ํด์ค๋ค. ๊ฒ์์ด๋ ์ธํ๊ธฐ๋ฅผ ๊ฒ์ํด์ฃผ์๋ค.
(์ธํ๊ธฐ๋ฅผ ๊ฒ์ํ ๊ฒฐ๊ณผ)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- jstl :: ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ค์ด, taglib ์ ์ธ-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
td, h2{
text-align: center;
}
table{
background-color: yellow;
}
</style>
</head>
<body>
<h2>+++++++++ ์ํ์ ๋ณด๋ฅผ ์ถ๋ ฅํฉ๋๋ค ++++++++++++</h2>
<table border="2" align='center' width="50%">
<thead>
<tr>
<th>์์ด๋</th><th>์ํ๋ช
</th><th>์ ์กฐ์ฌ</th><th>๊ฐ๊ฒฉ</th>
</tr>
</thead>
<tbody>
<c:forEach items="${list}" var="vo">
<tr>
<td>${vo.id}</td>
<td>${vo.name}</td>
<td>${vo.maker}</td>
<td>${vo.price}</td>
</tr>
</c:forEach>
</tbody>
</table>
<p></p>
<center><a href="product.jsp">์ํ๊ฐ์
ํ๊ธฐ ํ์ผ๋ก...</a></center>
</body>
</html>
์ด์ ๋ ๋ฉ์ธํ์ด์ง์์ ๊ฒ์์ด ์์ ๋ณด๊ธฐ ๋ฅผ ๋๋ฅด๋ฉด report.do
๋ก ์์ฒญ์ด ๋ค์ด๊ฐ๋ค.
์ฌ๊ธฐ์ AOP
๊ธฐ์ ์ด ๋ค์ด๊ฐ๋ค.
์ํ๋ช
์ด ๊ฒ์์ด ๋๋ฉด Advice
ํด๋์ค๊ฐ ๊ฐ์
ํด ์ผ๋ง๋ ๋ง์ด ๊ฒ์ํ๋์ง ๊ฒ์์ด๋ฅผ ์ธ์ด์ผ ํ๋ค.
์ด๋ AOP
๊ฐ ์ฌ์ฉ๋๋ค.
์ผ๋จ ๋จผ์ ๊ฒ์์ด๋ฅผ ์ ์ฅํ๊ธฐ ์ํด report
๋ผ๋ ํ
์ด๋ธ์ ์๋ก๋ง๋ค์ด ์ฃผ์๋ค.
1
2
word -- ๊ฒ์์ด
cnt --๊ฒ์์ด๊ฐ ์ผ๋ง๋ ๋ง์ด ๊ฒ์๋์๋์ง
ํ
์ด๋ธ์ด ์๋ก ์ถ๊ฐ๋์์ผ๋ฏ๋ก ํด๋น DB ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ธฐ ์ํด mapping.xml
์ ์์ฑํด์ผํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ns.sql.reportMapper">
<insert id="insertReport" parameterType="string">
INSERT INTO report VALUES(#{VALUE}, 1)
</insert>
<update id="updateReport" parameterType="string">
UPDATE report SET cnt = cnt+1 WHERE word=#{VALUE}
</update>
<select id="selectReport" resultType="hashmap">
<![CDATA[
SELECT word, cnt, ranking FROM(
SELECT word, cnt, rank() over(ORDER BY cnt DESC) as ranking
FROM report
)
WHERE ranking <= 5
]]>
</select>
</mapper>
์ xml
ํ์ผ์ ๊ธฐ์ค์ผ๋ก DAO
ํด๋์ค์ Service
ํด๋์ค๋ฅผ ๋ง๋ค์ด ์ฃผ์.
๋จผ์ DAO
ํด๋์ค์ด๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// dao class
package com.service.spring.aop.model;
@Repository
public class ReportDAOImpl implements ReportDAO{
@Autowired
private SqlSessionTemplate sqlSession;
public static final String NS="ns.sql.reportMapper.";
@Override
public void insertReport(String word) throws SQLException {
sqlSession.insert(NS+"insertReport",word);
}
@Override
public int updateReport(String word) throws SQLException {
return sqlSession.update(NS+"updateReport",word);
}
@Override
public List selectReport() throws SQLException {
return sqlSession.selectList(NS+"selectReport");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Service class
package com.service.spring.aop.model;
@Service
public class ReportServiceImpl implements ReportService{
@Autowired
private ReportDAO reportDAO;
@Override
public List selectReport() throws SQLException {
return reportDAO.selectReport();
}
@Override
public void saveReport(String word) throws SQLException {
int result=reportDAO.updateReport(word);
System.out.println(result+"***************************");
if(result==0) //0์ด๋ผ๋ ๊ฒ์ ์ต์ด๋ก ๊ฒ์๋ ๊ฒ์์ด๋ผ๋ ์๋ฏธ์ด๋ค. ๋ฐ๋ผ์ DB์ ๋ฃ์ด์ค๋ค.
reportDAO.insertReport(word);
}
}
reportService
์ ํจ์๋ 2๊ฐ
selectReport
๋ญํน ์์ ๊ฒฐ๊ณผํ์ด์ง
saveReport
์ฌ๊ธฐ์
AOP
๊ฐ ์ ์ฉ๋๋ค
์ด์ report.do
์ ์์ฒญ์ ์ฒ๋ฆฌํ Controller
๋ฅผ ๋ง๋ค์ด์ค๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.service.spring.aop.controller;
@Controller
public class ReportController {
@Autowired
private ReportService reportService;
@RequestMapping("/report.do")
public String selectReport(Model model)throws Exception{
model.addAttribute("list", reportService.selectReport());
return "report_result";
}
}
์ฌ๊ธฐ์๋ ๊ทธ๋ฅ ์์ ๋ฆฌ์คํธ๋ง ๋ฐ์์ model
์ ์ ์ฅํด์ค๋ค.
์ด์ AOP
๊ฐ ๋ค์ด๊ฐ๋ ๋ถ๋ถ์ ๋ณด์.
Advice
์ ํด๋์ค๋ฅผ ๋ณด์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// advice class
package com.service.spring.aop.advice;
@Component
@Aspect
@EnableAspectJAutoProxy //ํด๋น Annotation์ ์ฌ์ฉํ๋ฉด xmlํ์ผ์ ์๋ฌด๊ฒ๋ ์ธ ํ์๊ฐ ์๋ค
public class ReportAspect {
@Autowired
private ReportService reportService;
@Around("execution(* com.service..*Service*.find*(String))")
public Object report(ProceedingJoinPoint pjp)throws Throwable{
Object retValue;
retValue = pjp.proceed();//target ๋ฉ์๋๊ฐ ํธ์ถ..
System.out.println("target call...");
List list = (List)retValue;
if(!list.isEmpty()) { //์ฐพ์ list๊ฐ ์์ผ๋ฉด... cnt=cnt+1๋ก updateํด์ค์ผ ํ๋ค.
Object[ ] params=pjp.getArgs();
reportService.saveReport(params[0].toString());
}
return list;
}
}
com.service
ํจํค์ง ๋๋ ๊ทธ ํ์ ํจํค์ง์ ์๋, ์ด๋ฆ์ด Service
๋ก ๋๋๋ ํด๋์ค์, find
๋ก ์์ํ๊ณ String
ํ์
์ ์ธ์๋ฅผ ๋ฐ๋ ๋ชจ๋ ๋ฉ์๋๋ฅผ ๋์์ผ๋ก ํ๋ค.
๋ฐ๋ก
1
2
3
4
5
6
7
package com.service.spring.service;
public interface MyProductService {
int addProduct(MyProduct vo) throws Exception;
public List<MyProduct> findByProductName(String name)throws Exception;
}
findByProductName
์ด๋ค.
findByProductName()
์ด ํธ์ถ๋๋ฉด Targetํด๋์ค์ด๋ค.
์ด์ ๊ฒฐ๊ณผํ์ด์ง์ธ report_result.jsp
๋ฅผ ๋ณด์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container text-center">
<div class="jumbotron">
<h3>AOP๋ฅผ ์ด์ฉํด์ ๊ฒ์์ด ํต๊ณ๋ณด๊ธฐ</h3>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>๋ญ ํน</th><th>๊ฒ์์ด</th><th>์กฐํ์</th>
</tr>
</thead>
<c:forEach var="reportMap" items="${list}">
<tr>
<td>${reportMap.RANKING}</td>
<td>${reportMap.WORD}</td>
<td>${reportMap.CNT}</td>
</tr>
</c:forEach>
</table>
</div>
</body>
</html>
Spring Boot
- Spring Boot๋
Tomcat server
๊ฐ ๋ด์ฅ๋์ด์ ธ ์๋ค. - Spring Boot๋
web.xml
์ด ์ ๊ณต๋์ง ์๋๋ค. Context Path
๊ฐ ์กํ์ง ์๋๋ค. ์๋ค.
com.service.spring
ํ์์ ๋ชจ๋ ๊ฒ์ ๋ง๋ค์ด์ผ ํ๋ค.
๊ทธ๋์ผ Spring Boot
์ ์ํฅ์ ๋ฐ์ ์ ์๋ค.
๋จผ์ Spring Boot
์๋ฒ๋ฅผ ๊ฐ๋์์ผ์ผ ํ๋ค.
๋ฐ๋ก Sq18Boot1Application.java
๋ฅผ ์คํ์ํจ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
package com.service.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Sp18Boot1Application {
public static void main(String[] args) {
SpringApplication.run(Sp18Boot1Application.class, args);
}
}