spring中,AOP是第二特色,底层的实现就是基于动态代理,包括jdk代理和cglib代理.废话不多说,直接自己来时间一把。
jdk代理:也就是接口代理。
第一步:创建接口:略,详见IOC篇
第二步:创建接口的实现:详见IOC篇
第三步:创建jdk代理类
package com.pian.design.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Auther: Administrator
* @Date: 2020/2/11 0011 20:39
* @Description:
*/
public class JdkUtilProxy {
public static Object getProxy(final Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
//proxy,就是返回的object
//method,目标对象的方法
//args,目标方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy 记录日志");
return method.invoke(target,args);
}
});
}
}
第四步:测试test1方法
package com.pian;
import com.pian.design.Factory.MyBeanFactory;
import com.pian.design.proxy.CglibUtilProxy;
import com.pian.design.proxy.JdkUtilProxy;
import com.pian.dto.User;
import com.pian.service.TestIocService;
import com.pian.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @Auther: Administrator
* @Date: 2020/2/11 0011 20:44
* @Description:
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {App.class})
public class TestProxy {
private TestIocService iocService = (TestIocService)MyBeanFactory.getBean("testIocService");
@Autowired
private UserService userService;
@Test
public void test1(){
//使用jdk代理,iocService目标对象
TestIocService proxy = (TestIocService)JdkUtilProxy.getProxy(iocService);
//使用代理执行
proxy.save();
}
@Test
public void test2(){
//使用cglib代理,userService目标对象,无接口实现
UserService proxy = (UserService)CglibUtilProxy.getProxy(userService,UserService.class);
//使用代理执行
User user =proxy.selectById(2);
System.out.println(user);
}
}
cglib代理
第一步:创建实现类:略
第二步:创建cglib代理:
package com.pian.design.proxy;
import com.pian.service.UserService;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Auther: Administrator
* @Date: 2020/2/11 0011 20:53
* @Description:
*/
public class CglibUtilProxy {
public static Object getProxy(Object target,Class targetClz){
System.out.println("target.getClass():" + target.getClass());
return Enhancer.create(targetClz, new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("CglibUtilProxy 记录日志");
return method.invoke(target,args);
}
});
}
}
第三步:测试test2方法,见上图
是不是超简单,那理解了原理,当然要运用下,spring中给我们提供aop。
AOP编程:
第一步:引入AOP包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步:编写aspect类,这个类其实和一般类没什么区别,就是用来织入的。
在类上加上@Aspect 注解,表示是切面类,@Componet可以被扫描,
这里方法recodeLog就是通知,@Around就是要切入的方法,比如UserService下所有的方法。
我们的切面=通知+切入点
package com.pian.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @Auther: Administrator
* @Date: 2020/2/11 0011 20:19
* @Description:
*/
@Component
@Aspect
@Slf4j
public class MyAspect {
@Around("execution(public * com.pian.service.UserService.*(..))")
public void recodeLog(ProceedingJoinPoint jp){
String methodName = jp.getSignature().getName();
Object result = null;
try {
log.info("【环绕增强中的--->前置增强】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
//执行目标方法
result = jp.proceed();
log.info("【环绕增强中的--->返回增强】:the method 【" + methodName + "】 ends with " + result);
} catch (Throwable e) {
result = "error";
log.info("【环绕增强中的--->异常增强】:the method 【" + methodName + "】 occurs exception " + e);
}
log.info("【环绕增强中的--->后置增强】:-----------------end.----------------------");
return;
}
}
第四步:在启动类上加上@EnableAspectJAutoProxy,开启Aspect注解
第五步:测试
/**
* @Auther: Administrator
* @Date: 2020/2/11 0011 21:31
* @Description:
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {App.class})
public class TestAspect {
@Autowired
private UserService userService;
@Test
public void testSelectById(){
User user = userService.selectById(2);
System.out.println(user);
}
}
执行结果:
0211 213816.079- INFO[ main] com.pian.aspect.MyAspect -29: 【环绕增强中的--->前置增强】:the method 【selectById】 begins with [2]
0211 213816.112- INFO[ main] com.zaxxer.hikari.HikariDataSource -110: HikariPool-1 - Starting...
0211 213816.117- WARN[ main] com.zaxxer.hikari.util.DriverDataSource -68: Registered driver with driverClassName=oracle.jdbc.driver.OracleDriver was not found, trying direct instantiation.
0211 213816.347- INFO[ main] com.zaxxer.hikari.pool.PoolBase -516: HikariPool-1 - Driver does not support get/set network timeout for connections. (oracle.jdbc.driver.T4CConnection.getNetworkTimeout()I)
0211 213816.352- INFO[ main] com.zaxxer.hikari.HikariDataSource -123: HikariPool-1 - Start completed.
0211 213816.544- INFO[ main] com.pian.aspect.MyAspect -32: 【环绕增强中的--->返回增强】:the method 【selectById】 ends with User(userid=2, userName=老虎, pwd=null, age=10, sex=女, birthday=Sun Feb 09 21:26:23 CST 2020)
0211 213816.545- INFO[ main] com.pian.aspect.MyAspect -37: 【环绕增强中的--->后置增强】:-----------------end.----------------------
完美:这样我们就可以在发起请求时记录开始,请求返回后记录日志,这样方便以后定位问题。希望能帮助到大家