重点!java反射技术,动态代理AOP理念

重点!java反射技术,动态代理AOP理念

1.反射之前对于创建一个类的对象的操作

我们以一个Person类举例,我把Person粘贴在下面

public class Person {

    private String name;
    public int age;



    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person() {
        System.out.println("p");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void show(){
        System.out.println("你好,我是一个人");
    }
    private String showNation(String nation){
        System.out.println("我的国籍是"+nation);
        return nation;
    }
}

反射之前对于Person类的操作

//反射之前对于Person类的操作
@Test
public void test1(){
    Person person = new Person("Tom",12);

    person.age=10;
    System.out.println(person);

    person.show();

    //在person类的外部,不可以通过其person类对象调用其内部的私有结构
    //比如:私有属性,私有方法,私有构造器
}

2.Class类的理解,以及获取Class实例的方式

Class类的理解

1.类的加载过程:

程序经过javac.exe 生成一个或者多个字节码文件(.class结尾),java.exe 对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,此过程称为类的加载。加载到内存中的类,我们称为运行时类,就充当了一个Class类的实例。

类也是对象,万事万物皆对象 哈哈哈

2.换句话说,Class的实例就对应着一个运行时类

3.我们加载到内存中的运行时类会缓存一定的时间,在此时间之内我们可以通过不同的方式,来获取运行时类

获取Class实例的方式(前三种方式需要掌握)

@Test
public void test3() throws ClassNotFoundException {
    //方式一:调用运行时类的属性
    Class clazz1=Person.class;
    System.out.println(clazz1);

    //方式二:通过运行时类的对象进行获取,调用getClass方法
    Person p1 = new Person();
    Class clazz2 = p1.getClass();
    System.out.println(clazz2);

    //方式三:调用Class静态方法,类的全路径
    Class clazz3 = Class.forName("reflectionTest.test.Person");
    System.out.println(clazz3);

    System.out.println(clazz1==clazz2);
    System.out.println(clazz1==clazz3);

    //方式四:使用类的加载器ClassLoder
    ClassLoader classLoader = Reflection.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("reflectionTest.test.Person");
    System.out.println(clazz4);
    System.out.println(clazz1==clazz4);
}

3.使用反射做1同样的操作创建对象并操作

//使用反射,做上方同样的操作
@Test
public void test2() throws Exception, IllegalAccessException, InvocationTargetException, InstantiationException {

    //1.通过反射,创建Person类的对象
    /*
    这个方法是通过使用指定的构造器来创建对象
    如果使用clazz.newInstance()来创建对象,那么默认使用的就是空参的构造器
    所以这个时候就体现出了为什么JavaBean要求一定要有空参构造器,以及我们在造
    类的时候造空参构造器的必要性。
    */
    Class clazz = Person.class;
    Constructor cons=clazz.getConstructor(String.class,int.class);
    Person person=(Person) cons.newInstance("Tom",12);
    System.out.println(person);

    //2.通过反射,调用指定的属性
    Field age = clazz.getDeclaredField("age");
    age.set(person, 10);
    System.out.println(person);

    //3.通过反射,调用指定的方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(person);
    System.out.println("*************************************");

    //4.通过反射,可以调用Person类的私有结构的
    Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person) cons1.newInstance("Jerry");
    System.out.println(p1);

    //调用私有的属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1, "HanMeimei");
    System.out.println(p1);

    //调用私有的方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    String nation = (String) showNation.invoke(p1, "中国");
    System.out.println(nation);

    //问题1:反射机制与面向对象中的封装性是不是矛盾的,如何看待两个技术?
    //不矛盾。
    //问题2:开发中用哪种方式建对象?
    //建议:直接new的方式
    //什么时候会用:动态代理等,AOP理念会着重使用
}

4.总结反射中Class类对象常用的方法

以下返回的都是数组类型
getFields():获取当前运行时类及其父类声明为public访问权限的属性
getDeclaredFields():获取当前运行时类当中声明的所有属性(不包含父类中声明的属性,和权限无关,所有的都有)
getMethods():获取当前运行时类中所有声明为public的方法 包括父类中的
getDeclaredMethods():获取当前运行时类中声明的所有方法(不包含父类中声明的)
getConstructors():获取当前运行时类中声明为public的构造器,包含父类
getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
getSuperclass():获取运行时类的父类
getGenericSuperclass():获取运行时类的带泛型的父类
getInterfaces():获取运行时类实现的接口
clzz.getPackage():获取运行时类所在的包
getAnnotations():获取运行时类的注解

5.获取运行时类带泛型的父类的泛型代码

/*
   获取运行时类带泛型的父类的泛型
 */
@Test
public void test4(){
    Class clzz=Person.class;
    Type genericSuperclass = clzz.getGenericSuperclass();
    ParameterizedType parameterizedType= (ParameterizedType) genericSuperclass;
    //获取泛型类型的参数数组
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    System.out.println(actualTypeArguments[0].getTypeName());
}

6.调用运行时类的属性,方法,构造器

举例说明了

调用属性

@Test
public void testFiled1() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
    Class clazz= Person.class;
    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();

    //1.getDeclaredField(String Filedname):获取运行时类中指定变量名的属性
    Field name = clazz.getDeclaredField("name");
    //2.保证当前属性是可以访问的
    name.setAccessible(true);
    //3.获取设置指定对象的此属性值
    name.set(p, "TOM");

    System.out.println(p);
}

调用方法

/*
如何操作运行时类中的指定的犯法--需要掌握
 */
@Test
public void testMethod() throws Exception, InstantiationException {
    Class clazz= Person.class;
    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();
    //1.获取指定的某个方法
    /*
    getDeclaredMethod():参数1 :指明获取的方法的名称   参数2 指明获取的方法的形参列表
     */
    Method show = clazz.getDeclaredMethod("show", String.class);
    //2.保证当前的方法是可以用的
    show.setAccessible(true);
    //3.调用invoke方法执行
    /*
    invoke():参数1:方法的调用者    参数2:给方法形参赋值的实参
    invoke()的返回值即为对应调用的方法的返回值
     */
    Object chn = show.invoke(p, "chn");
    System.out.println(chn);

    System.out.println("*********************************************************");
    /*
    调用静态方法
     */
    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    //如果调用运行时类中的方法没有返回值,则此invoke()返回null
    Object invoke = showDesc.invoke(Person.class);
    System.out.println(invoke);

}

调用构造器

/*
如何调用运行时类中指定的构造器
 */
@Test
public void test8() throws Exception {
    Class clazz= Person.class;
    //1.获取指定的构造器
    //getDeclaredConstructor()参数 指明构造器的参数列表
    Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
    //2.保证此构造器是可以访问的
    declaredConstructor.setAccessible(true);
    //3.调用此构造器创建运行时类的对象
    Object tom = declaredConstructor.newInstance("Tom");
    System.out.println(tom);
}

7.静态代理与动态代理

什么是代理类:通常来讲我们的代理类和被代理类实现同一个接口,所以代理类里也有和被代理一样的同名方法。代理类在调用方法a的时候同时调用被代理类的同名方法a。这样就可以在调用被代理类方法a之前做一些事情,之后也可以做一些事情了。

image-20210309114443367

就是图片中这样

静态代理:就是代理类和被代理类在编译期间就定了

静态代理举例

/**
 * 静态代理举例
 *
 * 特点:代理类和被代理类在编译期间就确定下来了
 * @Author FanYuping
 * @Date 2021/3/8 17:26
 *
 */

interface ClothFactory{
    void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{
    private ClothFactory factory;

    public ProxyClothFactory(ClothFactory factory){
        this.factory=factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("--------------执行之前做一些准备工作!---------------");

        factory.produceCloth();

        System.out.println("--------------执行之后做一些后续的收尾工作!---------------");

    }
}
//被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("被代理类中的方法被执行");
    }
}

public class StaitcProxy {
    public static void main(String[] args) {
        NikeClothFactory nike=new NikeClothFactory();
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
        proxyClothFactory.produceCloth();
    }

}

动态代理:给一个被代理类,动态的生成一个代理类对象。反射的主要体现,动态性。

动态代理举例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *
 * 动态代理举例
 *
 * @Author FanYuping
 * @Date 2021/3/9 11:00
 */

interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "i believe i can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃"+food);
    }
}
class HumanUtil{
    public void method1(){
        System.out.println("----------TON1------------------");
    }

    public void method2(){
        System.out.println("----------TON2------------------");
    }
}

//代理类书写--动态代理
//根据加载到内存中的被代理类动态的创建一个代理类及其对象
//通过代理类的对象调用方法时,动态的调用被代理类中的同名方法

class ProxyFactory{
    //通过调用此方法,返回一个代理类对象
    public static Object getProxyInstance(Object obj) throws IllegalAccessException, InstantiationException {
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);

    }
}

class MyInvocationHandler implements InvocationHandler{
    private Object obj;//被代理类对象
    //赋值方法
    public void bind(Object obj){
        this.obj=obj;
    }
    //当我们通过代理类的对象,调a方法时,就会自动的调用如下的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method :即为代理类对象调用的方法,被代理类也调用同名方法
        //obj本身就是被代理对象
        HumanUtil util=new HumanUtil();
        util.method1();
        Object returnValue = method.invoke(obj, args);
        util.method2();
        return returnValue;
    }
}
public class ProxyTest {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        SuperMan superMan=new SuperMan();
        //代理类对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        proxyInstance.eat("四川麻辣烫");
        System.out.println("*******************************************************");
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyInstance1.produceCloth();
    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fan_Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值