代理模式
1.1、简介
定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。不改变原有业务的基础上,实现功能增强。
1.2、组成
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
1.3、静态代理
角色分析:
- 抽象角色:一般会使用接口或抽象类来解决。
- 真实角色:被代理的角色。
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。
- 客户:访问代理对象的人。
1.3.1、案例一:房东租房
1、接口
public interface UserService {
void add();
void delete();
void update();
void query();
}
2、真实角色
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("add方法");
}
public void delete() {
System.out.println("delete方法");
}
public void update() {
System.out.println("update方法");
}
public void query() {
System.out.println("query方法");
}
}
3、代理角色
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("【Debug】调用了"+msg+"方法");
}
}
4、客户
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.setUserService(userService);
userServiceProxy.add();
}
}
1.3.2、案例二:学生写论文
1、接口
public interface ThsisWriter {
void write();
}
2、学生类
public class Student implements ThsisWriter {
public void write() {
System.out.println("========学生写论文");
}
}
3、代理类
public class ThsisWriterProxy implements ThsisWriter {
private ThsisWriter thsisWriter;
public ThsisWriterProxy(ThsisWriter thsisWriter){
this.thsisWriter = thsisWriter;
}
public void write() {
//前置增强
System.out.println("需求分析");
thsisWriter.write();
//后置增强
System.out.println("提供源码");
}
}
4、测试类
public class TestThsis {
@Test
public void test(){
Student student = new Student();
ThsisWriterProxy tw = new ThsisWriterProxy(student);
tw.write();
}
}
静态代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公众的业务
- 公共业务也就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低。
1.4、动态代理
-
动态代理和静态代理角色一样
-
动态代理的代理类是动态生成的,不是直接写好的!
-
动态代理分为两大类:基于接口的动态代理;基于类的动态代理
-
基于接口----JDK动态代理【我们在这里使用】
-
基于类-------cglib
-
ava字节码实现:javasist
需要了解两个类:
1)Proxy:提供了创建动态代理类和实例的静态方法。
2)InvocationHandler:是由代理实例的调用处理程序实现的接口
动态代理的好处:
-
抽象角色:一般会使用接口或抽象类来解决
-
真实角色:被代理的角色
-
代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
-
客户:访问代理对象的人
-
一个动态代理类代理的是一个接口,一般就是对应的一类业务
-
一个动态代理类可以代理多个类,只要是实现了同一个接口即可
1.4.1、JDK代理
案例一
1、接口
public interface Rent {
void rent();
}
2、真实角色
public class Host implements Rent {
public void rent() {
System.out.println("房东要租房子");
}
}
3、自动生成代理类
//用这个类自动生成代理类,继承该接口是为了回调
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射实现
Object result = method.invoke(rent, args);
return result;
}
}
4、客户
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setRent(host);
//这里的proxy就是动态生成的,我们并没有写代理类!
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
案例二
1、学生类
public class Student implements ThsisWriter {
public void write() {
System.out.println("========学生写论文");
}
}
2、动态代理类
/**
* 动态代理类
* 根据接口在运行期动态创建代理对象
*/
public class ProxyBean implements InvocationHandler {
private Object service;
//括号中是logger的名字
private Logger logger = Logger.getLogger("");
// 创建代理对象
public Object newProxy(Object service){
this.service = service;
//代理类对象
// ------1.把this传入目的 为了回调invoke()
// ------2.类加载器和接口 为了依据接口创建对应的代理对象 创建代理的业务对象的方法method。
return Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),this);
}
/**
*
* @param proxy 代理对象
* @param method 被代理对象执行的方法
* @param args 执行方法传入的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.info("市场需求分析");
//执行原有业务对象方法,method 是原有业务对象的方法
Object result = method.invoke(service, args);
logger.info("加钱提供源码");
return result;
}
}
3、测试类
public class TestProxyBean {
@Test
public void testProxyBean(){
//原有的业务对象
Student s = new Student();
//动态代理对象
ProxyBean proxyBean = new ProxyBean();
//使用动态代理对象,动态生成代理对象,返回的是接口类型,不是原有业务类型
ThsisWriter thsisWriter = (ThsisWriter)proxyBean.newProxy(s);
//执行代理对象的方法
thsisWriter.write();
}
}
1.4.2、cglib代理
cglib介绍:
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,
它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供
方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM(ASM是一种通用Java字节码操作和分析框架。它可以用于修改现有的class文件或动态生成class文件)来生成java的字节码。
案例:
- 使用cglib实现代理需要先导入pom依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
该类为业务类的子类,即两个类是继承关系。
该类是根据业务对象,动态创建代理对象。
- 代理类需要实现MethodInterceptor类,为了回调
自定义代理类
//该类是用来创建代理类的类,需要实现一个MethodInterceptor接口,用来回调。
//该类和业务类的关系是父子关系,即该类继承了业务类。
/**
* 配置pom.xml依赖 cglib
* 动态代理类
* 1.动态创建代理对象------- 依据业务对象
* 2.业务类和代理对象是父子继承关系
* 3.实现 MethodInterceptor 为了回调
*/
public class MyCglibProxy implements MethodInterceptor {
//创建一个代理类
public Object newMyCglibInterceptor(Object service){
//引入依赖之后才会有此类
Enhancer enhancer = new Enhancer();
//设置业务类为代理类父类
enhancer.setSuperclass(service.getClass());
//设置回调为当前对象intercept方法
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
/**
*
* @param o 代理类对象
* @param method 业务类的方法,即被代理对象的方法
* @param objects 参数
* @param methodProxy 代理类对象的方法
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/**
* m(){
* super.m();
* }
*/
Object result = methodProxy.invokeSuper(o,objects);
return result;
}
}
注意:jdk代理和cglib代理是有区别的.
- cglib代理是将原有的业务类当作自己的父类,也就是说它和原有业务类是父子关系。可以从中直接调用原有业务类的方法。
- jdk代理是和原有业务类继承相同的接口,从而将接口注入到代理类中,从而达到调用原有业务类方法的目的。
以上就是本人我总结的一些代理类知识点,希望对大家有帮助!