Java 反射、代理

目录

      • 反射(reflect)
        • 获取class对象的三种方式
        • 使用反射创建对象
        • 使用反射操作字段
        • 使用反射调用方法
        • 使用反射动态创建、操作数组
      • 代理(proxy)
        • 静态代理
        • jdk动态代理
        • cglib动态代理
        • 总结

 

反射(reflect)

反射:通过类的class对象来获取类的信息,动态操作类中的字段、调用类中的方法。
 

获取class对象的三种方式

// Class.forName("全限定类名")
Class<?> class1 = Class.forName("com.chy.mall.model.User");  //必须写成全类名。类名是String形式,编译时并不知道Class对象的类型,所以是?
Class<User> class2 = (Class<User>)class1;  //强转


//类名.class
Class<User> class3=User.class;


//对象名.getClass()
User user=new User();
Class<? extends User> class4=user.getClass();

 

使用反射创建对象

Class<User> userClass = User.class;  //获取class对象
Constructor<User> constructor = userClass.getConstructor(int.class, String.class, int.class);  //通过class对象获取指定的构造器,形参表要写成class对象的形式
User user = constructor.newInstance(1, "chy", 20);  //传入实参创建实例

 

使用反射操作字段

Class<User> userClass = User.class;
Field nameField = userClass.getDeclaredField("name");  //获取私有字段
nameField.setAccessible(true);   //取消权限检查。private不允许在类外操作,需要取消权限检查才能操作private成员

User user = new User(1, "zhangsan", 20);
String name = (String) nameField.get(user);  //传入对象,获取该字段的值,返回Object类型
nameField.set(user, "lisi");  //设置对象该字段的值

 

使用反射调用方法

Class<User> userClass = User.class;

Method getNameMethod = userClass.getMethod("getName");  //获取无参的公有方法
Method getNameMethod = userClass.getMethod("setName", String.class);  //获取带参的公有方法。后面是形参表的class对象,参数个数可变

User user = new User(1, "zhangsan", 20);
String name = (String) getNameMethod.invoke(user);  //调用指定对象的该方法(无参),返回值是Object类型
setNameMethod.invoke(user, "lisi");  //调用指定对象的该方法(带参),后面是实参表,参数个数可变

 

使用反射动态创建、操作数组

//动态创建数组
Object obj = Array.newInstance(String.class, 10); //数组元素类型、元素个数,返回值是Object类型
String[] arr= (String[]) obj;  //有需要的话可以强转

//动态操作数组。传入数组的元素类型是无法预测的,所以参数是Object类型
Object first = Array.get(obj, 0);  //获取数组指定位置(index)上的元素,返回Object
Array.set(obj, 0, "zhangsan");  //设置数组指定位置上的元素

 
反射的特点是动态操作,传入该类的对象,根据传入的对象动态进行操作。

 

代理(proxy)

代理是在原类的基础上进行扩展、包装,使之变成更加强大的类,代替原来的类来使用。aop就是使用代理实现的。
 
代理分为2种

  • 静态代理
  • 动态代理,常见的有jdk动态代理、cglib代理

 

静态代理

//实现目标接口或继承目标类
public class UserDaoProxy implements UserDao{
    private UserDao userDao;  //把要代理的接口或类写作成员变量。声明为接口,则可以代理这个接口所有的实现类;声明为实现类,则只代理这个实现类

    //注入目标对象
    public UserDaoProxy(UserDao userDao) {
        this.userDao = userDao;
    }

	@Override
    public void addUser() {
        //.....   //前增强
        userDao.addUser();  //调用目标类的方法,如果是继承目标类,通过super来调用
        //.....  //后增强
    }

	@Override
    public void deleteUser() {
        userDao.deleteUser();  //如果不需要增强,直接调用目标类的方法即可
    }

}
UserDao userDao = new UserDaoImpl();
UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
userDaoProxy.addUser();    

 
静态代理的特点

  • 需要实现目标接口或者继承目标类
  • 可以代理接口、类
  • 可以设置每个方法的增强,很灵活;但要手动设置每个方法的增强,又很繁琐
     

不管实不实现目标接口、继不继承目标类,都可以做到对目标接口、目标类的增强,为什么要实现目标接口、继承目标类,没必要?

不管是参数还是返回值,都是用目标接口、目标类来声明,不会用代理类来声明,实现、继承的意义在于成为子类,可以作为目标类使用,在目标类出现的地方都可以使用,代理要做的是取代,并非只是增强。

 

jdk动态代理

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


//不需要继承、实现目标接口、目标类
public class UserDaoProxyFactory {
    private UserDao userDao;  //jdk动态代理代理的是接口,要声明为接口
    
    //注入目标对象
    public UserDaoProxyFactory(UserDao userDao) {
        this.userDao = userDao;
    }

    
    //获取代理,代理是目标接口的实例
    public UserDao getProxyInstance(){
        ClassLoader classLoader = userDao.getClass().getClassLoader();  //获取目标类的类加载器。
        Class<?>[] interfaces = userDao.getClass().getInterfaces();  //获取目标类实现的所有接口的class对象,这也是为什么要声明为接口的原因
        
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  //后2个参数是目标方法、实参表
                //.....   //前增强
                Object returnValue=method.invoke(userDao,args);  //调用目标方法,传入目标对象、实参表
                //....  //后增强
                return returnValue;  //返回目标方法的返回值,Object类型
            }
        };

        //创建代理,返回值是Object类型,需要强转
        Object userDaoProxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        //强转为目标接口类型
        return (UserDao)userDaoProxy;
    }
    
}
UserDaoImpl userDao = new UserDaoImpl();

//传入目标对象,创建代理,代理是目标接口类型
UserDao userDaoProxy = new UserDaoProxyFactory(userDao).getProxyInstance();

userDaoProxy.addUser();    

看invoke()的参数,在编译时不能确定要增强的方法,运行时根据传入的参数动态进行增强,所以叫做动态代理。静态代理在编译时就确定了要增强的方法。

是使用jdk自带的反射实现的动态代理,所以叫做jdk动态代理。
 

jdk动态代理的特点

  • 不需要实现接口、继承目标类
  • 代理的是接口
  • 通过代理调用的目标类的方法都会被增强,且所使用的增强完全一样,偏死板

 

cglib动态代理

需要添加cglib的依赖

<dependency>
    <groupId>cglibgroupId>
    <artifactId>cglibartifactId>
    <version>3.3.0version>
dependency>
//需要实现MethodInterceptor接口
public class UserDaoProxyFactory implements MethodInterceptor {
    private UserDao userDao;    //可以是接口,也可以是类

    //传入目标对象
    public UserDaoProxyFactory(UserDao target) {
        this.userDao = target;
    }

    //拦截目标方法,进行增强
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //.....  //前增强
        Object returnValue = method.invoke(userDao, objects);  //调用目标对象的方法
       //.....  //后增强
        return returnValue;
    }

    //创建代理,代理是目标接口类型
    public UserDao getProxyInstance(){
        Enhancer en = new Enhancer();  //创建工具类对象
        en.setSuperclass(userDao.getClass());  //设置基类(父类),即继承目标类
        en.setCallback(this);  //设置回调函数,此句代码是调用intercept()拦截目标方法,进行增强
        return (UserDao) en.create();  //创建并返回代理对象。创建的对象是Object型,需要强转
    }
    
}
UserDao userDao = new UserDaoImpl();

//传入目标对象,创建代理,代理是目标类型
UserDao userDaoProxy = new UserDaoProxyFactory(userDao).getProxyInstance();

userDaoProxy.addUser();

 
cglib动态代理的特点

  • 需要实现MethodInterceptor接口
  • 可以代理接口、类
  • 通过代理调用的目标类的方法都会被增强,且所使用的增强完全一样,偏死板

 

总结

如果要对不同的方法做不同的增强,用静态代理;如果对每个方法的增强都一样,用动态代理。

如果要代理接口,使用三种都行;如果要代理类,用静态代理、cglib动态代理。

你可能感兴趣的:(Java,SE,反射,reflect,代理,proxy)