SpringAOP和SpingMVC底层实现使用了代理模式。
代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点有:
那么如何解决以上提到的缺点呢?答案是可以使用动态代理方式
代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。
代理模式的主要角色如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OIBkjsgz-1622388001621)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210528171646601.png)]
**静态代理模式:**由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
角色分析:
●抽象角色: 一般会使用接口或者抽象类来解决
●真实角色:被代理的角色
●代理角色:代理真实角色,代理真实角色后,我们一般会做- -些附属操作
●客户:访问代理对象的人。
**代理模式的好处: **
●可以使真实角色的操作更加纯粹!不用去关注一-些公共的业务
●公共也就就交给代理角色!实现了业务的分工!
●公共业务发生扩展的时候,方便集中管理!
缺点:
●一个真实角色就会产生一 个代理角色;代码量会翻倍** 开发效率会变低**
代码步骤:
1.接口
package GOF23.proxy.demo01;
//出租房子
public interface Rent {
public void rent();
}
2.真实角色
package GOF23.proxy.demo01;
//出租房子
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
3.代理角色
package GOF23.proxy.demo01;
//代理房东出租房子
public class Proxy implements Rent{
private Host host;
public Proxy(){
}
public Proxy(Host host){
this.host=host;//直接代理房东租房子。
}
@Override
public void rent() {
host.rent();
fare();
}
// 中介收中介费
public void fare(){
System.out.println("收中介费");
}
}
4.客户端访问代理角色
package GOF23.proxy.demo01;
//租客
public class Tenant {
public static void main(String[] args) {
Host host=new Host();
// host.rent();
Proxy proxy = new Proxy(host);//中介帮房东租房子
proxy.rent();//会调用中介的方法,比如中介要收费用,此时我们只面对中介
}
}
**动态代理模式:**在程序运行时,运用反射机制动态创建而成
package GOF23.proxy.demo2;
public class UserServicelmpl implements UserSerevice{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void query() {
System.out.println("查找了一个用户");
}
}
package GOF23.proxy.demo2;
public class Client {
public static void main(String[] args) {
UserServicelmpl userServicelmpl =new UserServicelmpl();
userServicelmpl.add();
userServicelmpl.update();
userServicelmpl.delete();
userServicelmpl.query();
}
}
package GOF23.proxy.demo2;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
public interface UserSerevice {
public void add();
public void update();
public void delete();
public void query();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kEAKxlfn-1622388001623)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530204008984.png)]
假设此时需要加一个日志功能,此时就使用代理模式非常适合,因为不用改变原代码
没有使用代理的情况,,代码如下,
package GOF23.proxy.demo2;
public class UserServicelmpl implements UserSerevice{
@Override
public void add() {
System.out.println("日志:此时增加了一个用户");
System.out.println("增加了一个用户");
}
@Override
public void update() {
System.out.println("日志:此时更新了一个用户");
System.out.println("更新了一个用户");
}
@Override
public void delete() {
System.out.println("日志:此时删除了一个用户");
System.out.println("删除了一个用户");
}
@Override
public void query() {
System.out.println("日志:此时查找了一个用户");
System.out.println("查找了一个用户");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WkTjMcBF-1622388001624)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530204025832.png)]
可见,对原代码进行了更改,我们是不推荐这样操作。那么,此时就使用代理模式非常适合,因为不用改变原代码
代码如下
增加了UserServiceProxy 类
package GOF23.proxy.demo2;
public class UserServiceProxy implements UserService {
private UserServicelmpl userService;
public void setUserService(UserServicelmpl userService){
this.userService=userService;
}
// 日志方法
public void log(String msg){
System.out.println("日志:使用了"+msg+"方法!");
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void query() {
log("query");
userService.query();
}
}
结构如下
package GOF23.proxy.demo2;
public class Client {
public static void main(String[] args) {
UserServicelmpl userServicelmpl =new UserServicelmpl();
// userServicelmpl.add();
// userServicelmpl.update();
// userServicelmpl.delete();
// userServicelmpl.query();
UserServiceProxy proxy= new UserServiceProxy();
proxy.setUserService(userServicelmpl);
proxy.add();
proxy.update();
proxy.delete();
proxy.query();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mKi1Rqf7-1622388001625)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530212236430.png)]
动态代理和静态代理角色一-样
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
基于接口–JDK动态代理
基于类: cglib
java字节码实现:
两个类:
Proxy :代理
InvocationHandler :调用处理程序
动态代理的好处:
●可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
●公共也就就交给代理角色!实现了业务的分工!
●公共业务发生扩展的时候,方便集中管理!
●一 个动态代理类代理的是一个接口, 一般就是对应的一 类业务
●一个动态代理类可以代理多个类,只要是实现了同一一个接口即可!|
使用工具ProxyInvocationHandler对UserServicelmpl设置动态代理。
1、 ProxyInvocationHandler
package GOF23.proxy.dome4;
//动态代理公式
import GOF23.proxy.demo3.Rent;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
//此步骤固定不变,下次用只用改“rent”.
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制实现,
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
// 日志方法
public void log(String msg){
System.out.println("日志:使用了"+msg+"方法!");
}
}
2、Client
package GOF23.proxy.dome4;
import GOF23.proxy.demo2.UserService;
import GOF23.proxy.demo2.UserServicelmpl;
import java.lang.annotation.Target;
public class Client {
/*
public static void main(String[] args) {
//真实角色
Host host=new Host();
//代理角色,不存在
ProxyInvocationHandler pih=new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
Rent proxy=(Rent) pih.getProxy();//这里的proxy就是动态生成的
proxy.rent();
*/
public static void main(String[] args) {
/*
使用工具ProxyInvocationHandler对UserServicelmpl设置动态代理。
*/
//真实角色
UserServicelmpl userService=new UserServicelmpl();
//代理角色,不存在
ProxyInvocationHandler pih=new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象(设置要代理的对象)
pih.setTarget(userService);
//动态生成代理类
UserService proxy=(UserService) pih.getProxy();
proxy.add();
proxy.delete();
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pl0WNek-1622388001626)(C:\Users\蜗牛\AppData\Roaming\Typora\typora-user-images\image-20210530231842749.png)]
UserServicelmpl userService=new UserServicelmpl();
//代理角色,不存在
ProxyInvocationHandler pih=new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象(设置要代理的对象)
pih.setTarget(userService);
//动态生成代理类
UserService proxy=(UserService) pih.getProxy();
proxy.add();
proxy.delete();
}
}
结果
[外链图片转存中...(img-5pl0WNek-1622388001626)]