目录
目录结构
Maven配置
代理模式定义
先来看代理模式的定义
代理模式:是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。
eg.房产中介代替业主卖房
静态代理(Static Proxy)
静态代理:静态代理中代理类与被代理类都需要实现同一个接口,这就说明我们的一个静态代理类只能代理一个类,并且还要事先知道我们要代理哪个类才能写代理类,如果我们有其他类还想使用代理那就必须再写一个代理类。然而在实际开发中我们是可能是有非常多的类是需要被代理的,并且事先我们可能并不知道我们要代理哪个类。所以如果继续使用静态代理反而会增加许多的工作量,并且效率低下,代码复用率也不好。
话不多说直接上代码,直接在test文件下,创建测试类即可
package com.msb.test;
public class Test1 {
public static void main(String[] args) {
Person person =new Person("张三");
Court court=new Lawyer(person);
court.doCourt();
}
}
// 接口
interface Court{
void doCourt();
}
// 代理类
class Lawyer implements Court{
private Person person;
public Lawyer(Person person) {
this.person = person;
}
@Override
public void doCourt() {
System.out.println("律师取证:视频证明张三当时正在旅游,不在案发现场");
System.out.println("律师总结:张三不可能去杀人");
person.doCourt();
}
}
// 被代理的类
class Person implements Court{
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void doCourt() {
System.out.println(name+"说:我没有杀人");
}
}
动态代理(Dynamic Proxy)
动态代理:动态代理可以针对于一些不特定的类或者一些不特定的方法进行代理,我们可以在程序运行时动态的变化代理的规则,代理类在程序运行时才创建的代理模式成为动态代理。这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们的在Java代码中的“指示”动态生成的
- Proxy 动态代理 JDK动态代理 面向接口
- cglib 动态代理 第三方动态代理 面向父类
Proxy动态代理(面向接口)
1.必须有接口和实现类
2.增强接口中定义的方法
3.只能读取接口中方法的上注解
package com.msb.testProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* @author Yang JiaLin
* @date 2022/7/3 11:04
* @description
*/
public class Test01 {
public static void main(String[] args) {
Dinner dinner=new Person("张三");
// 通过Porxy动态代理获得一个代理对象,在代理对象中,对某个方法进行增强
// ClassLoader loader,被代理的对象的类加载器
ClassLoader classLoader = dinner.getClass().getClassLoader();
// Class<?>[] interfaces,被代理对象所实现的所有接口
Class[] interaces= dinner.getClass().getInterfaces();
// InvocationHandler h,执行处理器对象,专门用于定义增强的规则
InvocationHandler handler = new InvocationHandler(){
// invoke 当我们让代理对象调用任何方法时,都会触发invoke方法的执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object proxy, 代理对象
// Method method,被代理的方法
// Object[] args,被代理方法运行时的实参
Object res=null;
if(method.getName().equals("eat")){
System.out.println("饭前洗手");
// 让原有的eat的方法去运行
res =method.invoke(dinner, args);
System.out.println("饭后刷碗");
}else{
// 如果是其他方法,那么正常执行就可以了
res =method.invoke(dinner, args);
}
return res;
}
};
Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler);
//dinnerProxy.eat("包子");
dinnerProxy.drink();
}
}
interface Dinner{
void eat(String foodName);
void drink();
}
class Person implements Dinner{
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝茶");
}
}
class Student implements Dinner{
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在食堂吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝可乐");
}
}
Cglib动态代理(面向父类)
先来看张图
package com.msb.testProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* @author Yang JiaLin
* @date 2022/7/3 11:04
* @description
*/
public class Test01 {
public static void main(String[] args) {
Dinner dinner=new Person("张三");
// 通过Porxy动态代理获得一个代理对象,在代理对象中,对某个方法进行增强
// ClassLoader loader,被代理的对象的类加载器
ClassLoader classLoader = dinner.getClass().getClassLoader();
// Class<?>[] interfaces,被代理对象所实现的所有接口
Class[] interaces= dinner.getClass().getInterfaces();
// InvocationHandler h,执行处理器对象,专门用于定义增强的规则
InvocationHandler handler = new InvocationHandler(){
// invoke 当我们让代理对象调用任何方法时,都会触发invoke方法的执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object proxy, 代理对象
// Method method,被代理的方法
// Object[] args,被代理方法运行时的实参
Object res=null;
if(method.getName().equals("eat")){
System.out.println("饭前洗手");
// 让原有的eat的方法去运行
res =method.invoke(dinner, args);
System.out.println("饭后刷碗");
}else{
// 如果是其他方法,那么正常执行就可以了
res =method.invoke(dinner, args);
}
return res;
}
};
Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler);
//dinnerProxy.eat("包子");
dinnerProxy.drink();
}
}
interface Dinner{
void eat(String foodName);
void drink();
}
class Person implements Dinner{
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝茶");
}
}
class Student implements Dinner{
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在食堂吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝可乐");
}
}
以上就是Spring中所设计的代理模式
总结
- 不用修改源代码或者是没办法修改源代码的情况下,增加对象功能,使用代理对象代替原来的对象去完成工能,进而达到拓展到目的。
- JDK Proxy面向接口的动态代理,一定要有接口和实现类的存在,代理对象增强的是实现类,在实现接口的方法重写的方法。
生成的代理对象只能转换成接口,而不能转换成被代理类
代理对象只能增强接口中定义的方法 ,实现类中其他和接口无关的方法是无法增强的
代理对象只能读取到接口中方法上的注解,不能读取到实现类方法上的注解
等等,还有需要注意的细节点很容易忘掉报error,也是面试中经常提问的点
Error:java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given←类中缺少了无参构造,增加一个无参构造函数,编译通过
面试拓展:很多类中都需要写无参构造方法?
答:因为在一个java类中如果没有写构造方法,JVM(Java虚拟机)会默认添加一个无参构造方法,但是当写了有参构造后,JVM(Java虚拟机)不会再默认添加一个,如果只写了一个带参构造,假如有一个子类继承了它,一般子类的无参构造默认去调用的是父类的无参构造(而此时父类并没有无参构造),这个时候就会报错了。若一个POJO类要有带参构造方法,要记得先写无参构造方法。防止类追溯时找不到上级,报错。