(三)Spring-静态代理和动态代理
一、代理模式
代理就是帮人做一些事情
为什么要学习代理模式?因为这就是SpringAOP的底层。SpringAOP是重点。
代理模式的分类:
- 静态代理
- 动态代理
二、静态代理
2.1 静态代理定义
角色分析:
- 抽象角色:一般真实角色和代理角色会有个共同角色,会使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色,我们一般会有一些附加操作
- 客户:访问代理对象的人。
原来模式:
代理模式:
2.2 实现步骤
1 代理对象和被4代理对象共同实现1个接口
为什么要共同实现一个接口?经过实践,其实代理和被代理之间不需要强制实现一个接口,也能实现类似代理的功能,但是还是建议共同实现一个接口
我个人理解为:
- 接口规范了需要共同实现的核心方法,强制实现类必须实现核心方法。比如租房,方便代理对象在核心方法调用被代理对象核心方法
- 使得程序解耦,都可以用共同实现接口来做定义,如RentService。
- 在调用者看来都是一个类RentService。
package com.happy.service;
public interface RentService {
void rent();
}
2 被代理对象(房东)
package com.happy.service.impl;
import com.happy.service.RentService;
public class Fangdong implements RentService{
@Override
public void rent() {
System.out.println("房东:房东签订合同,收钱");
}
}
3 代理对象(中介)
package com.happy.service.impl;
import com.happy.service.RentService;
public class Proxy implements RentService{
private RentService fangdong;
public Proxy(RentService fangdong) {
this.fangdong = fangdong;
}
public Proxy() {
}
public RentService getFangdong() {
return fangdong;
}
public void setFangdong(RentService fangdong) {
this.fangdong = fangdong;
}
@Override
public void rent() {
System.out.println("中介:带租客看房");
fangdong.rent();
System.out.println("中介:缴税");
}
}
4 调用访问者(租客)
package com.happy.service;
import com.happy.service.impl.Fangdong;
import com.happy.service.impl.Proxy;
import org.junit.Test;
public class Client {
@Test
public void rent(){
RentService fangdong = new Fangdong();
fangdong.rent();
System.out.println("============================");
//中介一般会在被代理对象以后,添加一些附加操作
RentService proxy = new Proxy(fangdong);
proxy.rent();
}
}
2.3 实现核心总结
1 代理对象一定要持有被代理对象的引用
2 代理对象和被代理对象实现共同接口
2.4 代理模式的好处
- 可以使真实觉得的操作更加纯粹!不用去关注一些公共的业务。
- 公共业务就交给了代理角色。
- 实现了业务的分工!
- 公共业务发生了扩展的时候,方便接种管理!
- 功能增强:当需要给原有代码增加非业务核心功能时,使用代理模式添加,可以避免修改原有代码。
- 控制访问:不让直接访问真实角色即被代理对象。
- 不在原有的基础上纵向开发,使用静态代理就是横向开发,就是AOP底层的实现机制。
2.5 代理模式的坏处
2.6 真实运用场景
实现的共同接口
package com.happy.service;
public interface UserService {
void add();
void delete();
void update();
void query();
}
被代理真实对象
package com.happy.service.impl;
import com.happy.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("add操作");
}
@Override
public void delete() {
System.out.println("delete操作");
}
@Override
public void update() {
System.out.println("update操作");
}
@Override
public void query() {
System.out.println("query操作");
}
}
代理对象
package com.happy.service.impl;
import com.happy.service.UserService;
public class UserServiceProxy implements UserService {
UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void add() {
log();
userService.add();
}
@Override
public void delete() {
log();
userService.add();
}
@Override
public void update() {
log();
userService.add();
}
@Override
public void query() {
log();
userService.add();
}
public void log() {
System.out.println("记录日志!");
}
}
调用方
package com.happy.service;
import com.happy.service.impl.UserServiceImpl;
import com.happy.service.impl.UserServiceProxy;
import org.junit.Test;
public class UserServiceTest {
@Test
public void testUserService(){
UserService userService = new UserServiceImpl();
// new UserServiceProxy(userService);
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.setUserService(userService);
userServiceProxy.delete();
userServiceProxy.query();
}
}
三、动态代理
3.1 动态代理定义
3.2 动态代理分为3大类
1 基于接口的动态代理-JDK动态代理
- JDK动态代理【我们在这里使用的】
- JDK动态代理要求被代理对象
必须要有接口
,没有接口不能使用! - 使用java反射包中的类和接口实现动态代理的功能。
- 反射包为java.lang.reflect,里面有三个核心类:
- InvocationHandler,Method,Proxy
2 基于类的动态代理-cglib动态代理
3 基于字节码实现-JAVAsist
- JAVAsist
3.3 JDK动态代理的三个核心相关类
1 Proxy
他是一个类,Proxy类,用于创建代理的对象(不需要创建代理类,直接代理对象)
-
之前创建对象都是new一个类的构造方法(),即new 代理类的构造方法,现在我们是使用Proxy类的方法,代替new使用。
-
如何使用:
-
newProxyInstance()方法
public static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
Proxy.newProxyInstance
因为与IllegalArgumentException
相同的原因而Proxy.getProxyClass
。
2 InvocationHandler
他是一个接口,调用处理程序,接口中只有一个方法invoke(),他表示你的代理要干什么
-
InvocationHandler
是由代理实例的*调用处理程序*
需要实现的接口 。每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的
invoke
方法。
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
- 第一个参数:Object proxy,jdk创建的代理对象,无需赋值
- 第二个参数:Method method,目标类中的方法,也不需要赋值,jdk提供。
- 第三个参数, Object[] args,目标方法的参数,jdk提供。
- 如何使用:
3 Method
他是一个类,反射Method类,表示方法,确切就是目标类的方法。通过method可以执行某个方法。
如何使用:
- 通过method可以执行某个目标类的方法,method.invoke()
- 注意method.invoke()的invoke是Method类里面的方法,和前面InvocationHandler的invoke()不一样,只是同名。
- method.invoke(目标对象,方法的参数),等同于静态代理的object.方法名()如proxy.rent()
4 小结:
通过上面三个结合使用,完成jdk动态代理的实现。
3.4 动态代理的好处
- 静态代理的好处,动态代理的好处都有
- 一个动态代理代理的是一个接口, 一般就是对应的一类业务
- 一个动态代理可以代理多个类,只要实现一个接口就可以,不需要写代理类。
而静态代理需要为每一个被代理类生成对应代理类
(静态代理和被代理类需要实现一个共同接口)。 - 所以动态代理是固定模式,对虽有被代理类都一样(只要实现了接口),可以把动态代理作为工具类
3.5 实现动态代理的步骤
1 实现接口,定义目标类要完成的功能
2 创建目标类,去实现接口
3 创建InvocationHandler接口的实现类
,在invoke方法中完成代理类的功能
package com.happy.service.impl;
import com.happy.service.RentService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//一、创建InvocationHandler的实现类
public class MyInvocationHandler implements InvocationHandler {
// 为了更加泛化,使得该类不再局限于UserService一个种类,使用Object
Object target;
// Object target;
public MyInvocationHandler() {
}
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
//method传入,假设我们已经拿到目标类即被代理类的方法了
vist();
// target.rent();
// 动态代理用method方法反射调用方法,可以实现动态调用所有方法
// 而以前的静态方法,这里最多调用一类接口的方法,代理的对象有限,如果需要代理其他接口,则需再写一个静态代理
res = method.invoke(target, args);
return res;
}
public void vist() {
System.out.println("租房前看房!");
}
}
4 使用Proxy类的静态方法
,创建代理对象,并把返回值转为接口类型。
package com.happy.service;
import com.happy.service.impl.Fangdong;
import com.happy.service.impl.MyInvocationHandler;
import com.happy.service.impl.UserServiceImpl;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class TestJDKProxy {
@Test
public void test() {
/* @CallerSensitive
public static Object newProxyInstance
(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
*/
// 二、创建代理对象,使用proxy
// 1.创建被代理目标对象(这个静态代理和动态代理都需要)
RentService userService = new Fangdong();
// 2.创建InvocationHandler对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
// 3.动态通过构造器或者setter注入被代理对象
myInvocationHandler.setTarget(userService);
// 4.创建动态代理对象
RentService proxyInstance = (RentService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), myInvocationHandler);
// 5.执行动态代理方法
proxyInstance.rent();
}
}
四、优化动态代理作为工具类
4.1 使用普通Object
因为动态代理为固定范式,可以将他提取为公共工具类。
package com.happy.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandlerUtils implements InvocationHandler {
Object target;
public MyInvocationHandlerUtils() {
}
public MyInvocationHandlerUtils(Object target) {
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy() {
/*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
* 传入目标类的classLoader等,所以目标类对象应该动态传入工具类
* */
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 代理要实现的方法
// 1.被代理类的方法
// 2.增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
before();
//method为jdk帮传入被代理类的method反射方法
res = method.invoke(target, args);
after();
return res;
}
public void before() {
System.out.println("before method!");
}
public void after() {
System.out.println("after method!");
}
}
package com.happy.service;
import com.happy.service.impl.Fangdong;
import com.happy.service.impl.MyInvocationHandler;
import com.happy.service.impl.UserServiceImpl;
import com.happy.utils.MyInvocationHandlerUtils;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class TestJDKProxy {
@Test
public void test() {
/* @CallerSensitive
public static Object newProxyInstance
(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
*/
// 二、创建代理对象,使用proxy
// 1.创建被代理目标对象(这个静态代理和动态代理都需要)
RentService userService = new Fangdong();
// 2.创建InvocationHandler对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
// 3.动态通过构造器或者setter注入被代理对象
myInvocationHandler.setTarget(userService);
// 4.创建动态代理对象
RentService proxyInstance = (RentService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), myInvocationHandler);
// 5.执行动态代理方法
proxyInstance.rent();
}
@Test
public void testJDKProxyUtils() {
Object fangdong = new Fangdong();
// MyInvocationHandlerUtils myInvocationHandler = new MyInvocationHandlerUtils(fangdong);
// 或者使用无参构造+set
MyInvocationHandlerUtils myInvocationHandler = new MyInvocationHandlerUtils();
myInvocationHandler.setTarget(fangdong);
// myInvocationHandler.getProxy 内部使用了Proxy类静态方法,传入target和myInvocationHandler,才能生成proxy
RentService proxy = (RentService) myInvocationHandler.getProxy();
proxy.rent();
}
}
4.2 使用泛型
使用泛型后,在new MyInvocationHandler的时候用泛型传入类型RentService,可以不再强转。
package com.happy.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//在实例化泛型类时,必须指定T的具体类型
public class MyInvocationHandlerUtils<T> implements InvocationHandler {
T target;
public MyInvocationHandlerUtils() {
}
public MyInvocationHandlerUtils(T target) {
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(T target) {
this.target = target;
}
public T getProxy() {
/*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
* 传入目标类的classLoader等,所以目标类对象应该动态传入工具类
* */
T object=null;
object= (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return object;
}
// 代理要实现的方法
// 1.被代理类的方法
// 2.增强
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
System.out.println("执行方法:"+method.getName());
before();
//method为jdk帮传入被代理类的method反射方法
res = method.invoke(target, args);
after();
return res;
}
public void before() {
System.out.println("before method!");
}
public void after() {
System.out.println("after method!");
}
}
package com.happy.service;
import com.happy.service.impl.Fangdong;
import com.happy.service.impl.MyInvocationHandler;
import com.happy.service.impl.UserServiceImpl;
import com.happy.utils.MyInvocationHandlerUtils;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class TestJDKProxy {
@Test
public void testJDKProxyUtils() {
RentService fangdong = new Fangdong();
// MyInvocationHandlerUtils myInvocationHandler = new MyInvocationHandlerUtils(fangdong);
// 或者使用无参构造+set
MyInvocationHandlerUtils<RentService> myInvocationHandler = new MyInvocationHandlerUtils<RentService>();
myInvocationHandler.setTarget(fangdong);
// myInvocationHandler.getProxy 内部使用了Proxy类静态方法,传入target和myInvocationHandler,才能生成proxy
RentService proxy = myInvocationHandler.getProxy();
proxy.rent();
}
}
五、静态代理VS动态代理
静态代理 | 动态代理 | |
---|---|---|
1 | 代理类是自己手工实现 的,自己创建一个java类 |
动态 生成代理类(我们不需要手工写和生成) |
2 | 你所要代理的目标类是确定的 | 所要代理的目标类不确定,可以给不同的目标类随时创建代理 |
3 | 修改了被代理类和代理类的共同接口的时候,只需要修改被代理类 | 修改了被代理类和代理类的共同接口的时候,除了需要修改被代理类,还需要修改代理类 |
注意:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。