8.代理模式
为什么要学习代理模式?
因为这是SpringAop的底层。
代理模式:
- 代理类
- 委托类
- 调用者
- 接口类
未使用代理模式:
当调用者要去调用委托类的方法时,直接通过委托类对象进行调用。
使用代理模式:
当调用者要去调用委托类的方法时,首先需要代理类和委托类实现同一个接口,然后通过调用代理类的方法,去间接调用委托类的方法。
例子:
-
租房人(调用者)—中介(代理类)-----房东(委托类) 接口:出租房子
-
结婚的人(调用者)—婚庆公司(代理类)----结婚举办(委托类) 接口:布置结婚场地
代理类和委托类都要实现同一个接口
代理模式:
- 静态代理
- 动态代理
下面我们将会用房子租赁这个例子。去实现静态代理和动态代理。
8.1静态代理
分析:
- 接口抽象类:房子出租Rent
- 被代理的类(委托类):房东Host
- 代理类:中介Proxy
- 调用类:客户Client
8.1.1静态代理实现:
接口:Rent
//出租房子接口
public interface Rent {
//出租房子
void rent();
}
委托类:Host
//委托类:房东
public class Host implements Rent {
//出租房子
public void rent() {
System.out.println("房东出租房子");
}
}
代理类:Proxy
//代理类:中介
public class Proxy implements Rent{
//代理对象
private Rent host;
//设置代理对象
public void setHost(Rent host) {
this.host = host;
}
//出租房子
public void rent() {
host.rent();
}
}
调用类:Client
//调用者:客户
public class Client {
public static void main(String[] args) {
//联系中介
Proxy proxy = new Proxy();
//中介代理的房东
proxy.setHost(new Host());
//签订合同,完成房子出租
proxy.rent();
}
}
分析:中介类代理房东类,当客户需要租赁房子时,间接用过中介类租赁房东的房子。
8.1.2静态代理实现面向切面编程:
面向切面编程的一些基本概念:
-
目标对象target:被增强的对象。
-
代理类:增强后的对象。
-
通知Advice:就是增强类,增强代码所在的类。
-
织入:是个动词,就是把增强的代码织入到被代理对象的过程
-
连接点:目标对象中的每一个方法都是连接点。
-
切入点:目标对象中被增强的方法是切入点。
-
切面:切面就是切入点和通知的组合。
业务接口:
//业务接口
public interface UserDao {
//添加用户
void addUser();
//删除用户
void deleteUser();
//查询用户
void searchUser();
//更新用户
void UpdateUser();
}
业务类:
//业务实现:目标类,目标类的每个方法都可以时连接点,当被增强后的方法被称为切入点
public class UserDaoImpl implements UserDao {
public void addUser() {
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
public void searchUser() {
System.out.println("查询用户");
}
public void UpdateUser() {
System.out.println("更新用户");
}
}
业务代理类:
//业务代理:代理类,代理可以增加代理对象的方法
public class UserDaoProxy implements UserDao {
//代理对象
private UserDao userDao;
//切面
private MyAspect myAspect;
//设置切面
public void setMyAspect(MyAspect myAspect) {
this.myAspect = myAspect;
}
//设置代理对象
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser() {
myAspect.beforeLog();
userDao.addUser();
myAspect.afterLog();
}
public void deleteUser() {
myAspect.beforeLog();
userDao.deleteUser();
myAspect.afterLog();
}
public void searchUser() {
myAspect.beforeLog();
userDao.searchUser();
myAspect.afterLog();
}
public void UpdateUser() {
myAspect.beforeLog();
userDao.UpdateUser();
myAspect.afterLog();
}
}
切面类:
//切面
public class MyAspect {
//切入的方法,即通知
public void beforeLog(){
System.out.println("前置日志");
}
//切入的方法,即通知
public void afterLog(){
System.out.println("后置日子");
}
}
调用类:
//调用者
public class UserService {
public static void main(String[] args) {
//获得代理类
UserDaoProxy userDaoProxy = new UserDaoProxy();
//设置代理对象
userDaoProxy.setUserDao(new UserDaoImpl());
//设置切面
userDaoProxy.setMyAspect(new MyAspect());
//调用方法
//添加用户
System.out.println("================");
userDaoProxy.addUser();
//删除用户
System.out.println("================");
userDaoProxy.deleteUser();
//查询用户
System.out.println("================");
userDaoProxy.searchUser();
//更新用户
System.out.println("================");
userDaoProxy.UpdateUser();
}
}
结果:
8.1.3总结
静态代理模式的优点:
- 以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点:
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
为了解决静态代理的缺点,我们可以通过动态代理实现。
下面对动态代理进行讲解。
8.2动态代理
- 代理类动态生成
- 动态代理分类:
- 基于接口的动态代理:jdk代理
- 基于类的动态代理: cglib
- java字节码实现:javasist
8.2.1jdk代理
jdk需要两个类:
-
代理创建类:java.lang.reflect.Proxy
- 用于动态创建代理类,newProxyInstance()
-
调用处理程序接口:java.lang.reflect.InvocationHandler
- 所有动态创建的代理类调用方法时,都会交给指定实现该接口的类处理
用图理解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e2137LaE-1612345680959)(Spring.assets/image-20201128023806966.png)]
动态代理实现:
Rent接口:
public interface Rent {
void rent();
}
Host类:
public class Host implements Rent {
public void rent() {
System.out.println("房东出租房子");
}
}
代理类:
public class JdkProxy implements InvocationHandler {
private Rent rent;
public Object createProxy(Rent rent){
this.rent = rent;
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(rent, args);
return object;
}
}
调用者:
public class Client {
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();
Rent proxy = (Rent)jdkProxy.createProxy(new Host());
proxy.rent();
}
}
动态代理实现面向切面编程:
接口:
//业务接口
public interface UserDao {
//添加用户
void addUser();
//删除用户
void deleteUser();
//查询用户
void searchUser();
//更新用户
void UpdateUser();
}
切面:
//切面
public class MyAspect {
//切入的方法,即通知
public void beforeLog(){
System.out.println("前置日志");
}
//切入的方法,即通知
public void afterLog(){
System.out.println("后置日子");
}
}
实现类:
//业务实现:目标类,目标类的每个方法都可以时连接点,当被增强后的方法被称为切入点
public class UserDaoImpl implements UserDao {
public void addUser() {
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
public void searchUser() {
System.out.println("查询用户");
}
public void UpdateUser() {
System.out.println("更新用户");
}
}
代理类:
public class UserDaoProxy implements InvocationHandler {
private Object object;
private MyAspect myAspect;
public void setObject(Object object) {
this.object = object;
}
public void setMyAspect(MyAspect myAspect) {
this.myAspect = myAspect;
}
public Object createProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myAspect.beforeLog();
Object obj = method.invoke(object, args);
myAspect.afterLog();
return obj;
}
}
调用类:
public class UserDaoService {
public static void main(String[] args) {
//动态代理创建类
UserDaoProxy userDaoProxy = new UserDaoProxy();
//设置切面
userDaoProxy.setMyAspect(new MyAspect());
//设置代理对象
userDaoProxy.setObject(new UserDaoImpl());
//增强类
UserDao proxy = (UserDao) userDaoProxy.createProxy();
//调用方法
//添加用户
System.out.println("================");
proxy.addUser();
//删除用户
System.out.println("================");
proxy.deleteUser();
//查询用户
System.out.println("================");
proxy.searchUser();
//更新用户
System.out.println("================");
proxy.UpdateUser();
}
}
8.2.3cglib代理
动态代理实现:
//代理类
public class CglibProxy implements MethodInterceptor {
//创建代理对象
public Object createProxy(Rent rent){
//创建一个动态类对象
Enhancer enhancer = new Enhancer();
//设置父类,即增强的类
enhancer.setSuperclass(rent.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
//回调函数
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object o1 = methodProxy.invokeSuper(o, objects);
return o1;
}
}
@Test
public void cglibProxy(){
CglibProxy cglibProxy = new CglibProxy();
Rent proxy = (Rent) cglibProxy.createProxy(new Host());
proxy.rent();
}
动态代理实现面向切面编程:
public class UserDaoProxy1 implements MethodInterceptor {
public Object createProxy(Object o){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(o.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
MyAspect myAspect = new MyAspect();
myAspect.beforeLog();
Object o1 = methodProxy.invokeSuper(o, objects);
myAspect.afterLog();
return o1;
}
}
动态代理的优点:
静态代理有的它都有,静态代理没有的,它也有!
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
- 一个动态代理 , 一般代理某一类业务
- 一个动态代理可以代理多个类,代理的是接口