文章目录
- 1. Spring AOP底层原理
- 2. 代理模式
- 3. 静态代理
- 4. 动态代理
- 4.1 jdk 实现动态代理
- 4.2 cglib 实现动态代理
- 4.3 jdk、cglib动态代理两者的区别
1. Spring AOP底层原理
- 创建容器 new applicationContext();
- Spring把所有的Bean进行创建,进行依赖注入;
- ioc.getBean(UserService.class)此时获取得到Bean并不是原本的UserService类,而是通过动态代理生成一个代理类,这个代理类继承自UserService类。
2. 代理模式
代理模式是一种比较好的设计模式;
- 使用代理对象来增强目标对象(target object),这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能;
- 将核心业务代码和非核心的公共代码分离解耦,提高代码的可维护性,让被代理类专注业务降低代码复杂度。
- 被代理类专注业务;
- 代理类非核心的公共代码;
通常使用代理实现比如拦截器、事务控制,还有测试框架 mock、用户鉴权、日志、全局异常处理等。
3. 静态代理
代理类:
package com.lize.demo.aop.static1;
public class CeoProxy extends Ceo{
@Override
public void meeting(String name) {
if(name.equals("张三")){
super.meeting(name);
}else{
System.out.println("登记:"+name);
}
}
}
被代理类:
package com.lize.demo.aop.static1;
public class Ceo {
public void meeting(String name){
System.out.println("接见客户:"+name);
}
}
测试:
package com.lize.demo.aop.static1;
import org.junit.jupiter.api.Test;
public class TestProxy {
@Test
public void test(){
Ceo ceo = new CeoProxy();
ceo.meeting("张三");
ceo.meeting("李四");
}
}
运行结果:
使用静态代理的弊端:一个代理类只能代理一个类型。
4. 动态代理
4.1 jdk 实现动态代理
jdk自带的,不需要额外导入其他依赖;
Proxy类中使用频率最高的方法是:newProxyInstance(),Proxy这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h
)
三个参数分别表示的意思是:
- loader:类加载器,用于加载代理对象,传入被代理类的类加载器;
- interfaces:被代理类实现的一些接口;被代理的类需要实现接口;
- h:实现了InvocationHandler接口的对象;
package com.lize.demo.aop.autoProxy;
public interface IUserService {
void add();
}
package com.lize.demo.aop.autoProxy;
// 被代理的类
public class UserService implements IUserService{
@Override
public void add() {
System.out.println("增加。。。");
}
}
package com.lize.demo.aop.autoProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyHandler implements InvocationHandler {
private Object target;
public MyHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置通知");
// args 传递过来的参数
Object res = method.invoke(target, args);
System.out.println("后置通知");
return res;
}
}
package com.lize.demo.aop.autoProxy;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Proxy;
public class TestProxy {
@Test
public void test(){
IUserService userService = (IUserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
UserService.class.getInterfaces(),
new MyHandler(new UserService())
);
userService.add();
System.out.println(userService.getClass());
}
}
运行结果:
4.2 cglib 实现动态代理
package com.lize.demo.aop.autoProxy2;
// 被代理的类
public class UserService {
public void add() {
System.out.println("增加。。。");
}
}
package com.lize.demo.aop.autoProxy2;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 做增强处理
public class MyCallback implements MethodInterceptor {
private Object target;
public MyCallback(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置通知");
Object res = proxy.invoke(target, args);
System.out.println("后置通知");
return res;
}
}
package com.lize.demo.aop.autoProxy2;
import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;
public class TestProxy {
@Test
public void test(){
Enhancer enhancer = new Enhancer();
// 设置被代理的类
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MyCallback(new UserService()));
UserService userService = (UserService) enhancer.create();
// 代理的类
userService.add();
System.out.println(userService.getClass());
}
}
4.3 jdk、cglib动态代理两者的区别
- jdk动态代理只能代理实现了接口的类;而cglib可以代理未实现任何接口的类;
- cglib动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为finnal类型的类和方法;
- 在Spring中默认使用jdk动态代理,判断目标类是否实现了接口,如果实现了接口使用jdk动态代理,否则使用cglib;在Spring Boot中默认使用的是cglib动态代理。
在Spring中,如果想修改动态代理方式,可以通过注解的方式进行修改。
@EnableAspectJAutoProxy(proxyTargetClass = true) // cglib
@EnableAspectJAutoProxy(proxyTargetClass = false) // jdk
在Spring Boot中,如果想修改动态代理方式,可以通过在配置文件application.properties中添加配置即可。
spring.aop.proxy-target-class=false