aop的使用场景
一般来说aop的使用场景有: 事务管理, 日志操作记录, 异常处理, 缓存(页面缓存,方法缓存/查询缓存,数据缓存(对Entity/数据记录进行缓存)), 权限安全认证, Persistence 持久化, Synchronization 同步, Lazy loading 懒加载, 性能监控(方法执行时间/调用次数)
事务管理
编程式事务和声明式事务,实际中使用声明式事务
声明式事务有:配置和注解两种方式
1.事务管理
<aop:aspectj-autoproxy />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" read-only="false" propagation="REQUIRED" />
<tx:method name="update*" read-only="false" propagation="REQUIRED" />
<tx:method name="delete*" read-only="false" propagation="REQUIRED" />
<tx:method name="create*" read-only="false" propagation="REQUIRED" />
<tx:method name="import*" read-only="false" propagation="REQUIRED" />
<tx:method name="remove*" read-only="false" propagation="REQUIRED" />
<tx:method name="submit*" read-only="false" propagation="REQUIRED" />
<tx:method name="check*" read-only="false" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor pointcut="execution(* com.able.service.*.*(..))" advice-ref="txAdvice" />
</aop:config>
----------------------------------------------------------------------
2.日志处理
2.1 日志切面
package com.manager.aspect;
3 import org.apache.log4j.Logger;
4 import org.aspectj.lang.JoinPoint;
5 import org.aspectj.lang.annotation.*;
6 import org.springframework.stereotype.Component;
7
8 import java.util.Arrays;
9
10 /**
11 * 日志切面
12 * Created by Kevin on 2017/10/29.
13 */
14 @Aspect
15 @Component
16 public class LogAspect {
17 /**
18 * 操作日志文件名
19 */
20 private static final String OPERATION_LOG_NAME = "operationLog";
21 private static final String LOG_FORMATTER = "%s.%s - %s";
22 Logger log = Logger.getLogger(OPERATION_LOG_NAME);
23 /**
24 * 对查询方法记录日志的切点
25 */
26 @Pointcut("execution(* com.manager..*.*Controller.query*(..))")
27 public void query(){}
28
29 /**
30 * 对新增方法记录日志的切点
31 */
32 @Pointcut("execution(* com.manager..*.*Controller.add*(..))")
33 public void add(){}
34
35 /**
36 * 对修改方法记录日志的切点
37 */
38 @Pointcut("execution(* com.manager..*.*Controller.update*(..))")
39 public void update(){}
40
41 /**
42 * 对删除方法记录日志的切点
43 */
44 @Pointcut("execution(* com.manager..*.*Controller.delete*(..))")
45 public void delete(){}
46
47 @AfterReturning(value = "query()", returning = "rvt")
48 public void queryLog(JoinPoint joinPoint, Object rvt) {
49 String className = joinPoint.getTarget().getClass().getName();
50 String methodName = joinPoint.getSignature().getName();
51 String returnResult = rvt.toString();
52 log.info(String.format(LOG_FORMATTER, className, methodName, returnResult));
53 }
54
55 @Before("add()")
56 public void addLog(JoinPoint joinPoint) {
57 String className = joinPoint.getTarget().getClass().getName();
58 String methodName = joinPoint.getSignature().getName();
59 Object[] params = joinPoint.getArgs();
60 log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));
61 }
62
63 @Before("update()")
64 public void updateLog(JoinPoint joinPoint) {
65 String className = joinPoint.getTarget().getClass().getName();
66 String methodName = joinPoint.getSignature().getName();
67 Object[] params = joinPoint.getArgs();
68 log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));
69 }
70
71 @Before("delete()")
72 public void deleteLog(JoinPoint joinPoint) {
73 String className = joinPoint.getTarget().getClass().getName();
74 String methodName = joinPoint.getSignature().getName();
75 Object[] params = joinPoint.getArgs();
76 log.info(String.format(LOG_FORMATTER, className, methodName, Arrays.toString(params)));
77 }
78 }
<!--启用AspectJ自动代理,其中proxy-target-class为true表示使用CGLib的代理方式,false表示JDK的代理方式,默认false-->
<aop:aspectj-autoproxy />
2.2 测试类
package com.manager.user.controller;
2
3 import com.fasterxml.jackson.databind.ObjectMapper;
4 import com.manager.user.pojo.User;
5 import org.junit.Before;
6 import org.junit.Test;
7 import org.junit.runner.RunWith;
8 import org.springframework.beans.factory.annotation.Autowired;
9 import org.springframework.http.MediaType;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 import org.springframework.test.context.web.WebAppConfiguration;
13 import org.springframework.test.web.servlet.MockMvc;
14 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
15 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
16 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
17 import org.springframework.web.context.WebApplicationContext;
18
19 import static org.junit.Assert.assertNotNull;
20
21 /**
22 * UserController单元测试
23 * Created by Kevin on 2017/10/26.
24 */
25 @RunWith(SpringJUnit4ClassRunner.class)
26 @ContextConfiguration({"classpath*:applicationContext.xml", "classpath*:spring-servlet.xml"})
27 @WebAppConfiguration
28 public class UserControllerTest {
29 @Autowired
30 private WebApplicationContext wac;
31 private MockMvc mvc;
32
33 @Before
34 public void initMockHttp() {
35 this.mvc = MockMvcBuilders.webAppContextSetup(wac).build();
36 }
37
38 @Test
39 public void testQueryUsers() throws Exception {
40 mvc.perform(MockMvcRequestBuilders.get("/users"))
41 .andExpect(MockMvcResultMatchers.status().isOk());
42 }
43
44 @Test
45 public void testAddUser() throws Exception {
46 User user = new User();
47 user.setName("kevin");
48 user.setAge(23);
49 mvc.perform(MockMvcRequestBuilders.post("/users")
50 .contentType(MediaType.APPLICATION_JSON_UTF8)
51 .content(new ObjectMapper().writeValueAsString(user)))
52 .andExpect(MockMvcResultMatchers.status().isOk())
53 .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("kevin"))
54 .andExpect(MockMvcResultMatchers.jsonPath("$.age").value(23));
55 }
56
57 @Test
58 public void testQueryUserById() throws Exception {
59 User user = new User();
60 user.setId(8);
61 mvc.perform(MockMvcRequestBuilders.get("/users/" + user.getId()))
62 .andExpect(MockMvcResultMatchers.status().isOk())
63 .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("kevin"))
64 .andExpect(MockMvcResultMatchers.jsonPath("$.age").value(23));
65
66 }
67
68 @Test
69 public void testUpdateUserById() throws Exception {
70 User user = new User();
71 user.setId(9);
72 user.setName("tony");
73 user.setAge(99);
74 mvc.perform(MockMvcRequestBuilders.put("/users/" + user.getId())
75 .contentType(MediaType.APPLICATION_JSON_UTF8)
76 .content(new ObjectMapper().writeValueAsString(user)))
77 .andExpect(MockMvcResultMatchers.status().isOk());
78 }
79
80 @Test
81 public void testDeleteUserById() throws Exception {
82 long id = 10;
83 mvc.perform(MockMvcRequestBuilders.delete("/users/" + id))
84 .andExpect(MockMvcResultMatchers.status().isOk());
85 }
86 }
3.性能监控
先配置pom文件如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>
${metrics.version}
</version>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-annotation</artifactId>
<version>${metrics.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.7</version>
</dependency>
----------------------------------------------------------------------
3.1 定义Configuration 配置类
@Configuration
@ComponentScan({ "com.keevol.springboot.metrics.lifecycle",
"com.keevol.springboot.metrics.aop" })
@AutoConfigureAfter(AopAutoConfiguration.class)
public class DropwizardMetricsMBeansAutoConfiguration {
@Value("${metrics.mbeans.domain.name:com.keevol.metrics}")
String metricsMBeansDomainName;
@Autowired
MBeanServer mbeanServer;
@Autowired
MetricRegistry metricRegistry;
@Bean
public JmxReporter jmxReporter() {
JmxReporter reporter = JmxReporte.forRegistry(metricRegistry)
.inDomain(metricsMBeansDomainName).registerWith(mbeanServer)
.build();
return reporter;
}
}
将这个配置类添加到 META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.keevol.springboot.metrics.autocfg.DropwizardMetricsMBeansAutoConfiguration
----------------------------------------------------------------------
3.2 定义一个监控切面
@Component
@Aspectpublic
class AutoMetricsAspect {
protected ConcurrentMap<String, Meter> meters = new ConcurrentHashMap<>();
protected ConcurrentMap<String, Meter> exceptionMeters = new ConcurrentHashMap<>();
protected ConcurrentMap<String, Timer> timers = new ConcurrentHashMap<>();
protected ConcurrentMap<String, Counter> counters = new ConcurrentHashMap<>();
@Autowired
MetricRegistry metricRegistry;
@Pointcut(value = "execution(public * *(..))")
public void publicMethods() {
}
@Before("publicMethods() && @annotation(countedAnnotation)")
public void instrumentCounted(JoinPoint jp, Counted countedAnnotation) {
String name = name(jp.getTarget().getClass(), StringUtils.hasLength(countedAnnotation.name()) ? countedAnnotation.name() : jp.getSignature().getName(), "counter");
Counter counter = counters.computeIfAbsent(name, key -> metricRegistry.counter(key));
counter.inc();
}
@Before("publicMethods() && @annotation(meteredAnnotation)")
public void instrumentMetered(JoinPoint jp, Metered meteredAnnotation) {
String name = name(jp.getTarget().getClass(), StringUtils.hasLength(meteredAnnotation.name()) ? meteredAnnotation.name() : jp.getSignature().getName(), "meter");
Meter meter = meters.computeIfAbsent(name, key -> metricRegistry.meter(key));
meter.mark();
}
@AfterThrowing(pointcut = "publicMethods() && @annotation(exMe-teredAnnotation)", throwing = "ex")
public void instrumentExceptionMetered(JoinPoint jp, Throwable ex, ExceptionMetered exMeteredAnnotation) {
String name = name(jp.getTarget().getClass(), StringUtils.hasLength(exMeteredAnnotation.name()) ? exMeteredAnnotation.name() : jp.getSignature().getName(), "meter", "exception");
Meter meter = exceptionMeters.computeIfAbsent(name, meterName -> metricRegistry.meter(meterName));
meter.mark();
}
@Around("publicMethods() && @annotation(timedAnnotation)")
public Object instrumentTimed(ProceedingJoinPoint pjp, Timed timedAnnotation) throws Throwable {
String name = name(pjp.getTarget().getClass(), StringUtils.hasLength(timedAnnotation.name()) ? timedAnnotation.name() : pjp.getSignature().getName(), "timer");
Timer timer = timers.computeIfAbsent(name, inputName -> metricRegistry.timer(inputName));
Timer.Context tc = timer.time();
try {
return pjp.proceed();
} finally {
tc.stop();
}
}
}
----------------------------------------------------------------------
3.3具体业务service
@Component
public class MockService {
@Timed
@Counted
public void doSth() {
System.out.println("just do something.");
}
}
----------------------------------------------------------------------
4.权限验证
使用拦截器实现 token登录验证
链接