提问:你可以按照网上教程写一个动态代理的案例;也可以写一个AOP的案例。也常听到AOP的底层就是动态代理,是否能解释的清楚它两之间的关系呢?
目录
- 一 无代理
- 二、静态代理改造
- 三 动态代理改造
- 四 spring AOP的实现
- 总结
一 无代理
public interface UserService {
void save(String name);
void delete(String id);
void update();
String findAll(String name);
String findOne(String id);
}
// 原始业务逻辑对象
public class UserServiceImpl implements UserService{
// 开启事务 处理业务 调用dao
@Override
public void save(String name) {
try {
System.out.println("开启事务");
System.out.println("处理事务逻辑,调用DAO~~~");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
System.out.println("处理事务逻辑,调用DAO~~~");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void update() {
try {
System.out.println("开启事务");
System.out.println("处理事务逻辑,调用DAO~~~");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public String findAll(String name) {
try {
System.out.println("开启事务");
System.out.println("处理事务逻辑,调用DAO~~~");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
return name;
}
@Override
public String findOne(String id) {
try {
System.out.println("开启事务");
System.out.println("处理事务逻辑,调用DAO~~~");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
return id;
}
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.save("1");
}
可以发现UserServiceImpl 中存在重复代码 和业务代码
重复代码
System.out.println("开启事务");
System.out.println("提交事务");
System.out.println("回滚事务");
业务代码
System.out.println("处理事务逻辑,调用DAO~~~");
我们希望将重复的非业务代码拆分出来,UserServiceImpl 专心写业务代码,创建一个代理类就负责重复的非业务代码
更新后的代码
二、静态代理改造
public interface UserService {
void save(String name);
void delete(String id);
void update();
String findAll(String name);
String findOne(String id);
}
// 原始业务逻辑对象
public class UserServiceImpl implements UserService{
// 开启事务 处理业务 调用dao
@Override
public void save(String name) {
System.out.println("处理事务逻辑,调用DAO~~~");
}
@Override
public void delete(String id) {
System.out.println("处理事务逻辑,调用DAO~~~");
}
@Override
public void update() {
System.out.println("处理事务逻辑,调用DAO~~~");
}
@Override
public String findAll(String name) {
System.out.println("处理事务逻辑,调用DAO~~~");
return name;
}
@Override
public String findOne(String id) {
System.out.println("处理事务逻辑,调用DAO~~~");
return id;
}
}
//静态代理类
//开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
public class UserServiceStaticProxy implements UserService{
// 依赖原始业务逻辑对象 // Target:目标对象|被代理对象称之为目标对象 原始业务逻辑对象
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void save(String name) {
try {
System.out.println("静态代理:开启事务");
// 调用原始业务逻辑对象的方法
userService.save(name);
System.out.println("静态代理:提交事务");
} catch (Exception e) {
System.out.println("静态代理:回滚事务");
e.printStackTrace();
}
}
@Override
public void delete(String id) {
try {
System.out.println("静态代理:开启事务");
// 调用原始业务逻辑对象的方法
userService.delete(id);
System.out.println("静态代理:提交事务");
} catch (Exception e) {
System.out.println("静态代理:回滚事务");
e.printStackTrace();
}
}
@Override
public void update() {
try {
System.out.println("静态代理:开启事务");
// 调用原始业务逻辑对象的方法
userService.update();
System.out.println("静态代理:提交事务");
} catch (Exception e) {
System.out.println("静态代理:回滚事务");
e.printStackTrace();
}
}
@Override
public String findAll(String name) {
try {
System.out.println("静态代理:开启事务");
// 调用原始业务逻辑对象的方法
String all = userService.findAll(name);
System.out.println("静态代理:提交事务");
return all;
} catch (Exception e) {
System.out.println("静态代理:回滚事务");
e.printStackTrace();
}
return null;
}
@Override
public String findOne(String id) {
try {
System.out.println("静态代理:开启事务");
// 调用原始业务逻辑对象的方法
String one = userService.findOne(id);
System.out.println("静态代理:提交事务");
return one;
} catch (Exception e) {
System.out.println("静态代理:回滚事务");
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserServiceStaticProxy proxy = new UserServiceStaticProxy();
proxy.setUserService(userService);
proxy.save("2");
}
可以发现,UserServiceImpl中只写了业务代码,其他的写在了静态代理中。
静态代理是无法满足实际场景的,需要使用动态代理才行,具体原因参考此文
三 动态代理改造
public interface UserService {
void save(String name);
void delete(String id);
void update();
String findAll(String name);
String findOne(String id);
}
// 原始业务逻辑对象
public class UserServiceImpl implements UserService{
// 开启事务 处理业务 调用dao
@Override
public void save(String name) {
System.out.println("处理事务逻辑,调用DAO~~~");
}
@Override
public void delete(String id) {
System.out.println("处理事务逻辑,调用DAO~~~");
}
@Override
public void update() {
System.out.println("处理事务逻辑,调用DAO~~~");
}
@Override
public String findAll(String name) {
System.out.println("处理事务逻辑,调用DAO~~~");
return name;
}
@Override
public String findOne(String id) {
System.out.println("处理事务逻辑,调用DAO~~~");
return id;
}
}
public class UserServiceStaticSynamicProxy implements InvocationHandler {
private Object target;
public void setUserService(Object target) {
this.target = target;
}
public Object getProxy(){
Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理:开启事务");
Object result = method.invoke(target, args);
System.out.println("代理:提交事务");
return result;
}
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserServiceStaticSynamicProxy proxy = new UserServiceStaticSynamicProxy();
proxy.setUserService(userService);
UserService proxy2 = (UserService)proxy.getProxy();
proxy2.findAll("3");
}
四 spring AOP的实现
这里提供一个伪代码:
我们自己写一个@Before,@After注解,被该注解标记的方法中就写非业务代码。
如被@Before标记的方法,实现”spring aop 动态代理:开启事务“
被@After标记的方法,实现”spring aop 动态代理:提交事务“
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
ApplicationContext.registerBean(Config.class);
Config o = (Config)ApplicationContext.map.get(Config.class.getName());
o.pointCut();
}
}
class Config {
// 自定义注解Before
@Before
public void before() {
System.out.println("before");
System.out.println("spring aop 动态代理:开启事务");
}
// 自定义注解After
@After
public void before() {
System.out.println("after");
System.out.println("spring aop 动态代理:提交事务");
}
// 被代理的方法
public void pointCut() {
System.out.println("pointCut");
System.out.println("处理事务逻辑,调用DAO~~~");
}
}
class ApplicationContext {
public static Map<String, Object> map = new HashMap<>();
// 参数传递Class字节码:如Config.class
public static void registerBean(Class clazz) {
Method beforeMethod = null;
Method afterMethod = null;
// 获取Config类全部的public属性方法
for (Method declaredMethod : clazz.getDeclaredMethods()) {
// 找到具有@Before注解的方法
Before annotation = declaredMethod.getAnnotation(Before.class);
if (annotation != null) {
beforeMethod = declaredMethod;
}
// 找到具有@After注解的方法
After annotation = declaredMethod.getAnnotation(After.class);
if (annotation != null) {
afterMethod = declaredMethod;
}
}
Set<String> proxyMethName = new HashSet<>();
for (Method declaredMethod : clazz.getDeclaredMethods()) {
Before annotation = declaredMethod.getAnnotation(Before.class);
if (annotation != null) {
continue;
}
After annotation = declaredMethod.getAnnotation(After.class);
if (annotation != null) {
continue;
}
proxyMethName.add(declaredMethod.getName());
}
// 这里使用cjlib的方式实现动态代理(没有接口的动态代理实现),之前实例的是jdk的动态代理实现(基于接口实现)
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(clazz);
// 设置enhancer的回调对象
Method finalBeforeMethod = beforeMethod;
Method finalAfterMethod = afterMethod;
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (proxyMethName.contains(method.getName()) && finalBeforeMethod != null) {
// 这一行打印:spring aop 动态代理:开启事务
finalBeforeMethod.invoke(o, objects);
}
// 调用被代理的方法(这一行打印:处理事务逻辑,调用DAO~~~)
Object invoke = methodProxy.invokeSuper(o, objects);
if (proxyMethName.contains(method.getName()) && finalAfterMethod != null) {
// 这一行打印:spring aop 动态代理:提交事务
finalAfterMethod .invoke(o, objects);
}
return invoke;
}
});
// 创建代理对象
Object o = enhancer.create();
map.put(clazz.getName(), o);
}
}
最原始的方式,我们是手写了一个最标准的动态代理模式。现在我们改造了一下,模仿spring实现动态代理。表面看是只是用了@Before
注解,还有什么切面概念,其实还是代理的逻辑。
这个时候你再看spring aop的实现过程,是不是特别清晰
spring利用注解实现aop
spring利用xml实现aop
总结
目的:将非业务代码抽离出来,我们自定义了动态代理解决;也利用了spring提供的方案解决了。而在 ”利用xml实现aop“ 中提供的示例中,作者表面未使用动态代理的写法,最内层确实是动态代理
回顾一下前面的问题:为什么说aop的底层就是动态代理。我是这样理解的:
在没有spring之前,我们可以使用自定义的动态代理方式将业务代码前后的非业务代码抽离出来(例如前面将非业务代码抽离到代理类中)。后来spring一琢磨,把这种行为定义为切面编程的思想,取了个英文名AOP。并把将我们之前首先动态代理过程改造了一下,套了一壳,包装了一下,换成了更简单的写法。
所以,当你想将业务代码前后的非业务代码抽离出来时,你可以考虑自己手写一个动态代理,也可以使用spring提供的写法。根据实际需求出发。